From af9cc8ce3a624f58b0c46457a942020e0a6704e1 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Mon, 10 Jul 2023 22:11:30 +0530 Subject: [PATCH 01/18] feat(codeOptimization): common pop up for all assets --- .../assets-browser.component.html | 131 ++++ .../assets-browser.component.scss | 387 ++++++++++++ .../assets-browser.component.spec.ts | 23 + .../assets-browser.component.ts | 582 ++++++++++++++++++ .../lib/questionset-editor-library.module.ts | 4 +- .../src/lib/services/config/label.config.json | 29 +- 6 files changed, 1154 insertions(+), 2 deletions(-) create mode 100644 projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html create mode 100644 projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.scss create mode 100644 projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts create mode 100644 projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html new file mode 100644 index 000000000..684758802 --- /dev/null +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html @@ -0,0 +1,131 @@ + +
{{selectast}}
+
+ + +
+
+ +

{{assetsCount}}

+
+
{{emptySearchMessage}}
+
+
+
+ {{data?.name}} +
+
+ +
+
+ +
+
+ +

{{assetsCount}}

+
+
{{emptySearchMessage}}
+
+
+
+ {{data?.name}} +
+
+
+ +
+
+
+
+ +
+
+ + + +
{{configService.labelConfig?.lbl?.uploadAndUse}}
+
+
+
+
+
{{chooseOrDragAst}}*
+
+ + +
{{assetName}}
+ {{configService.labelConfig?.lbl?.upload}} {{acceptedFileType}} ({{configService.labelConfig?.lbl?.maxFileSize}} + {{astSize}}{{astSizeType}}) +
+

{{errorMsg}}

+
+
+
+
+
    +
  • {{configService.labelConfig?.lbl?.allowedFileTypes}} {{acceptedFileType}}
  • +
  • {{configService.labelConfig?.lbl?.maximumAllowedFileSize}} {{astSize}}{{astSizeType}}
  • +
+
+
{{configService.labelConfig?.lbl?.copyRightsAndLicense}}*
+

{{termsAndCondition}}

+
+
+
+
+
    +
  • + + {{configService.labelConfig?.lbl?.dropChooseFile}}
  • +
+
+
+
+ +
+
+
+
+
+
+ + +
+
+ +
+
+
+
diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.scss b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.scss new file mode 100644 index 000000000..860b81fe8 --- /dev/null +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.scss @@ -0,0 +1,387 @@ +.editorWrapper { + border: 0px solid white; + } + + .editorWrapper.hasError { + border: 1px solid red; + border-radius: 2px; + } + + .characterCount { + text-align: right; + /* background-color: #f2f2f2; + border: 1px solid #c4c4c4; */ + border-top: 0; + padding-right: 7px; + font-size: 11px; + font-weight: bold; + margin-top: -16px; + position: relative + } + + .custom-image img { + border: 1px dotted; + padding: 7px; + margin: 6px; + cursor: pointer; + } + + .resource-image { + height: 180px !important; + } + + .asset_container { + overflow-y: auto; + overflow-x: hidden; + min-height: 300px !important; + max-height: 300px !important; + padding: 5px; + } + + .insert-image-btn { + position: absolute; + z-index: 1; + left: 653px; + background-color: transparent; + padding: 12px 14px !important; + margin-left: 6px; + } + + .insert-image-btn>.icon { + opacity: 1; + } + + .insert-image-btn:active { + background-color: transparent; + } + + .upload-file-section { + display: flex; + height: 240px; + width: 100%; + max-width: 800px; + align-items: center; + justify-content: center; + flex-direction: column; + margin: 0 auto; + background-color: #F5F9FC; + border: 1px dashed #80a7ce; + } + + .upload-file-description p { + color: #999999; + } + + .upload-file-description ul { + margin: 0; + list-style: disc; + } + + .upload-file-description ul li { + margin-bottom: 8px; + } + + .upload-file-description ul li a { + cursor: pointer; + font-size: 12px; + } + + + /* Grid Layout CSS for uploaded image & video section */ + .sb-grid-layout { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); + grid-gap: 16px; + } + + .sb-grid-layout.image { + grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); + } + + .sb-grid-layout.video { + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + } + + .sb-grid-layout .sb-video-content .sb-image-section { + height: 96px; + overflow: hidden; + border-radius: 4px; + box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.5); + } + + .sb-image-section { + .selected-video { + display: none; + } + + &.active { + .selected-video { + position: absolute; + right: 4px; + top: 4px; + color: #07bc81; + font-size: 20px; + display: block; + } + } + } + .sb-grid-layout .sb-video-content .sb-image-section img { + width: 100%; + height: 100%; + cursor: pointer; + } + + .overlay-image { + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.3); + position: absolute; + left: 0; + right: 0; + cursor: pointer; + } + + .overlay-image .play.icon { + position: absolute; + transform: translate(-50%, -50%); + top: 50%; + left: 50%; + color: rgba(255, 255, 255, .6); + font-size: 32px; + cursor: pointer; + } + + + + ::ng-deep { + + .ck-rounded-corners .ck.ck-editor__top .ck-sticky-panel .ck-toolbar, .ck.ck-editor__top .ck-sticky-panel .ck-toolbar.ck-rounded-corners { + border-left: none; + border-right: none; + border-bottom: 1px solid #c4c4c4; + border-radius: 0; + } + + .ck-rounded-corners .ck.ck-editor__main>.ck-editor__editable, .ck.ck-editor__main>.ck-editor__editable.ck-rounded-corners { + border: none; + } + + .ck-dropdown.ck-font-size-dropdown .ck-dropdown__panel { + height: 150px; + overflow-y: scroll; + } + + figure.image.ck-widget { + width: auto; + height: auto; + overflow: visible; + } + + figure.image img { + width: 100%; + } + + figure.image.resize-25 { + width: 25%; + height: auto; + } + figure.image.resize-50 { + width: 50%; + height: auto; + } + figure.image.resize-75 { + width: 75%; + height: auto; + } + figure.image.resize-100 { + width: 100%; + height: auto; + } + figure.table{ + margin : 2.2rem 0 1.5rem 1rem!important; + } + .text-center { text-align: center } + .text-left { text-align: left } + .text-right { text-align: right } + + .fs-8 { font-size: 0.5rem; } + .fs-10 { font-size: 0.625rem; } + .fs-12 { font-size: 0.75rem; } + .fs-14 { font-size: 0.875rem; } + .fs-16 { font-size: 1rem; } + .fs-18 { font-size: 1.125rem; } + .fs-20 { font-size: 1.25rem; } + .fs-22 { font-size: 1.375rem; } + .fs-24 { font-size: 1.5rem; } + .fs-26 { font-size: 1.625rem; } + .fs-28 { font-size: 1.75rem; } + .fs-30 { font-size: 1.875rem; } + .fs-36 { font-size: 2.25rem; } + } + + .upload-image-modal-section { + border-radius: 4px; + width: 100%; + min-height: 130px; + background: #F5F9FC; + border: 1px dashed #024f9d; + } + + .upload-input-button { + input[type="file"] { + position: absolute; + right: 0px; + top: 0px; + font-size: 118px; + margin: 0px; + padding: 0px; + cursor: pointer; + opacity: 0; + height: 100%; + } + } + + .upload-file-section { + display: flex; + height: 240px; + width: 100%; + max-width: 800px; + align-items: center; + justify-content: center; + flex-direction: column; + margin: 0 auto; + background-color: #F5F9FC; + border: 1px dashed #80a7ce; + } + + .qq-uploader.qq-uploader-selector.custom-qq-uploader { + background: inherit; + border-color: none; + border: none; + max-height: inherit; + min-height: inherit; + overflow-y: inherit; + width: 688px; + height: 240px; + display: flex; + justify-content: center; + align-items: center; + } + + .terms-and-condition { + line-height: 14px; + } + + .sb-info-bx{ + padding: 0.4rem !important; + li{ + margin: 0 !important; + padding: 0 !important; + &::before{ + content: "" !important; + } + } + } + + .red{ + color: red; + } + .b-bl{ + border-left: solid 1px #e4e1e1; + } + .sb-form-fields{ + .sb-field-group{ + margin: 1rem 0; + } + .sb-field{ + position: relative; + margin-bottom: 0.5rem; + .sb-form-control{ + border: 1px solid rgba(34,36,38,.15); + width: 100%; + border-radius: .28571429rem; + box-shadow: 0 0 0 0 transparent inset; + padding: .25rem .5rem!important; + } + } + } + @keyframes spinner-border { + to { + transform: rotate(360deg); + } + } + .sb-loading-spinner { + width: 1rem; + height: 1rem; + margin-right: 8px; + border-width: 0.2em; + display: inline-block; + vertical-align: text-bottom; + border: 0.15em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + -webkit-animation: spinner-border 0.75s linear infinite; + animation: spinner-border 0.75s linear infinite; + } + .sb-btn-loading:before { + position: absolute; + content: ""; + top: 50%; + left: 50%; + margin: -0.64285714em 0 0 -0.64285714em; + width: 1.28571429em; + height: 1.28571429em; + border-radius: 500rem; + border: 0.2em solid rgba(0, 0, 0, 0.15); + } + .sb-btn-loading:after { + position: absolute; + content: ""; + top: 50%; + left: 50%; + margin: -0.64285714em 0 0 -0.64285714em; + width: 1.28571429em; + height: 1.28571429em; + -webkit-animation: button-spin 0.6s linear; + animation: button-spin 0.6s linear; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + border-radius: 500rem; + border-color: #fff transparent transparent; + border-style: solid; + border-width: 0.2em; + -webkit-box-shadow: 0 0 0 1px transparent; + box-shadow: 0 0 0 1px transparent; + } + .sb-btn-outline-disabled { + background-color: #ffffff; + border: 1px solid #cccccc; + color: #999999; + cursor: default; + font-weight: 400; + } + .sb-color-grey{color:#666;} + + .flex-jc-space-end { + justify-content: flex-end !important; + } + .fs-0785{ + font-size: 0.785rem !important; + } + .ui.info.message{ + color: #276f86 !important; + } + .sb-textbox[disabled="true"]{ + opacity: 0.3 !important; + font-weight: 500 !important; + } + .sb-tabset-segment { + min-height: 288px; + max-height: 288px; + overflow-y: auto; + } + + input:focus-visible{ + border: none !important; + } + \ No newline at end of file diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts new file mode 100644 index 000000000..11de36c04 --- /dev/null +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AssetsBrowserComponent } from './assets-browser.component'; + +describe('AssetsBrowserComponent', () => { + let component: AssetsBrowserComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AssetsBrowserComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AssetsBrowserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts new file mode 100644 index 000000000..65a23c298 --- /dev/null +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -0,0 +1,582 @@ +import { Component, OnInit, AfterViewInit, Output, Input, EventEmitter, OnChanges, ViewChild, ElementRef, OnDestroy } from '@angular/core'; +import * as _ from 'lodash-es'; +import { catchError, map } from 'rxjs/operators'; +import { throwError, Observable } from 'rxjs'; +import { EditorService } from '../../services/editor/editor.service'; +import { ConfigService } from '../../services/config/config.service'; +import { QuestionService } from '../../services/question/question.service'; +import { config } from 'projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.data'; +import { ToasterService } from '../../services/toaster/toaster.service'; + +@Component({ + selector: 'lib-assets-browser', + templateUrl: './assets-browser.component.html', + styleUrls: ['./assets-browser.component.scss'] +}) +export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { + @ViewChild('editor') public editorRef: ElementRef; + @Output() videoDataOutput = new EventEmitter(); + @Output() editorDataOutput = new EventEmitter(); + @Input() editorDataInput: any; + @Input() videoShow; + @Input() assetType; + @Input() showAssetPicker; + @Output() assetBrowserEmitter = new EventEmitter(); + @Output() modalDismissEmitter = new EventEmitter(); + @ViewChild('modal') private modal; + myAssets = []; + allAssets = []; + selectedAsset = {}; + loading = false; + isClosable = true; + assetConfig: any = {}; + acceptAssetType: any; + initialized = false; + selectedAssetId: string; + showAddButton: boolean; + showErrorMsg: boolean; + errorMsg: string; + query: string; + showAssetUploadModal: boolean; + assetFormValid = false; + selectast: string; + myast: string; + allast: string; + emptySearchMessage: string; + chooseOrDragAst: string; + astSize: string; + astSizeType: string; + acceptedFileType: string; + + public assetData = {}; + public assetFile: any; + public formData: any; + public mediaobj; + public assetsCount: any; + public editorInstance: any; + public editorConfig: any; + public isAssetBrowserReadOnly = false; + public assetProxyUrl: any; + public initialFormConfig: any; + public formConfig: any; + public termsAndCondition: any; + public assetName: any; + public searchMyInput = ''; + public searchAllInput: any; + public assetUploadLoader = false; + constructor(private editorService: EditorService, public configService: ConfigService, + private questionService: QuestionService, public toasterService: ToasterService) { } + + ngOnInit() { + this.assetProxyUrl = _.get(this.editorService.editorConfig, 'config.assetProxyUrl') || _.get(this.configService.urlConFig, 'URLS.assetProxyUrl'); + this.initialFormConfig = _.get(config, 'uploadIconFormConfig'); + this.formConfig = _.get(config, 'uploadIconFormConfig'); + this.termsAndCondition = _.get(this.configService.labelConfig, 'termsAndConditions.001'); + this.assetConfig = this.editorService.editorConfig.config.assetConfig; + this.initialized = true; + this.selectast = this.configService.labelConfig?.lbl?.selectAsset[this.assetType]; + this.emptySearchMessage = _.get(this.configService.labelConfig?.emptySearchMessage[this.assetType]); + this.myast = this.configService.labelConfig?.lbl?.myAssets[this.assetType]; + this.allast = this.configService.labelConfig?.lbl?.allAssets[this.assetType]; + this.chooseOrDragAst = this.configService.labelConfig?.lbl?.chooseOrDragAsset[this.assetType]; + this.astSize = this.assetConfig[this.assetType].size; + this.astSizeType = this.assetConfig[this.assetType].sizeType; + this.acceptedFileType = this.assetConfig[this.assetType].accepted; + this.acceptAssetType = this.getAcceptType(this.assetConfig[this.assetType].accepted, this.assetType); + this.editorConfig = { + toolbar: ['heading', '|', 'bold', '|', 'italic', '|', 'underline', '|', 'BulletedList', '|', 'alignment', + '|', 'insertTable', '|', 'numberedList', '|', 'fontSize', '|', 'subscript', '|', 'superscript', '|', + 'MathText', '|', 'specialCharacters', '|' + ], + fontSize: { + options: [ + 'eight', + 'ten', + 'twelve', + 'fourteen', + 'sixteen', + 'eighteen', + 'twenty', + 'twentytwo', + 'twentyfour', + 'twentysix', + 'twentyeight', + 'thirty', + 'thirtysix' + ] + }, + image: { + resizeUnit: '%', + resizeOptions: [{ + name: 'resizeImage:25', + value: '25', + icon: 'small', + className: 'resize-25' + }, + { + name: 'resizeImage:50', + value: '50', + icon: 'medium', + className: 'resize-50' + }, + { + name: 'resizeImage:75', + value: '75', + icon: 'large', + className: 'resize-75' + }, + { + name: 'resizeImage:100', + value: '100', + icon: 'full', + className: 'resize-100' + }, + { + name: 'resizeImage:original', + value: null, + icon: 'original', + className: 'resize-original' + }], + toolbar: ['imageStyle:alignLeft', 'imageStyle:alignCenter', 'imageStyle:alignRight', '|', + 'resizeImage:25', 'resizeImage:50', 'resizeImage:75', 'resizeImage:100', 'resizeImage:original'], + styles: ['full', 'alignLeft', 'alignRight', 'alignCenter'] + }, + isReadOnly: false, + removePlugins: ['ImageCaption', 'mathtype', 'ChemType', 'ImageResizeHandles'] + }; + } + + ngOnChanges() { + if (this.videoShow) { + this.showAssetPicker = true; + this.selectAsset(undefined); + } + } + + getAcceptType(typeList, type) { + const acceptTypeList = typeList.split(', '); + const result = []; + _.forEach(acceptTypeList, (content) => { + result.push(`${type}/${content}`); + }); + return result.toString(); + } + + dismissAssetPicker() { + this.showAssetPicker = false; + if(this.assetType=='video') { + this.videoShow=false; + this.videoDataOutput.emit(false); + } else if(this.assetType == 'image') { + this.showAssetPicker = false; + this.modalDismissEmitter.emit({}); + } + } + + selectAsset(data) { + if (data) { + this.showAddButton = true; + this.selectedAssetId = data.identifier; + this.selectedAsset = data; + } else { + this.showAddButton = false; + this.selectedAssetId = ''; + this.selectedAsset = {}; + } + } + + getMyAssets(offset, query?, search?) { + this.assetsCount = 0; + if (!search) { + this.searchMyInput = ''; + if(this.assetType == 'video') { + this.selectAsset(undefined); + } + } + if (offset === 0) { + this.myAssets.length = 0; + } + let req; + req = { + filters: { + mediaType: [this.assetType], + createdBy: _.get(this.editorService.editorConfig, 'context.user.id') + }, + offset + }; + if (query) { + req.query = query; + } + this.questionService.getAssetMedia(req).pipe(catchError(err => { + let errInfo; + if (this.assetType == 'image') { + errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.022') }; + } else if (this.assetType == 'video') { + errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.023')}; + } + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + })).subscribe((res) => { + this.assetsCount = res.result.count; + _.map(res.result.content, (item) => { + if (item.downloadUrl) { + this.myAssets.push(item); + } + }); + }); + } + + getAllAssets(offset, query?, search?) { + this.assetsCount = 0; + if (!search) { + this.searchAllInput = ''; + } + if (offset === 0) { + this.allAssets.length = 0; + } + let req; + req = { + filters: { + mediaType: [this.assetType] + }, + offset + }; + if (query) { + req.query = query; + } + this.questionService.getAssetMedia(req).pipe(catchError(err => { + let errInfo; + if (this.assetType == 'image') { + errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.022') }; + } else if (this.assetType == 'video') { + errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.023') }; + } + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + })).subscribe((res) => { + this.assetsCount = res.result.count; + _.map(res.result.content, (item) => { + if (item.downloadUrl) { + this.allAssets.push(item); + } + }) + }); + } + + /** + * function to lazy load my assets + */ + lazyloadMyAssets() { + const offset = this.myAssets.length; + this.getMyAssets(offset, this.query, true); + } + + // search feature for images + searchAsset(event, type) { + if (event === 'clearInput' && type === 'myAssets') { + this.query = ''; + this.searchMyInput = ''; + } else if (event === 'clearInput' && type === 'allAssets') { + this.query = ''; + this.searchAllInput = ''; + } else { + this.query = event.target.value; + } + if (type === 'myAssets') { + this.getMyAssets(0, this.query, true); + } else { + this.getAllAssets(0, this.query, true); + } + } + + addAssetInEditor(videoModal?, assetUrl?, assetId?, assetName?) { + if (this.assetType == 'image'){ + const src = this.getMediaOriginURL(assetUrl); + const baseUrl = _.get(this.editorService.editorConfig, 'context.host') || document.location.origin; + this.mediaobj = { + id: assetId, + type: 'image', + src, + baseUrl + }; + this.editorInstance.model.change(writer => { + const imageElement = writer.createElement('image', { + src, + alt: assetName, + 'data-asset-variable': assetId + }); + this.editorInstance.model.insertContent(imageElement, this.editorInstance.model.document.selection); + }); + this.showAssetPicker = false; + this.showAssetUploadModal = false; + } else if (this.assetType == 'video') { + const videoData: any = _.cloneDeep(this.selectedAsset); + videoData.src = this.getMediaOriginURL(videoData.downloadUrl); + videoData.thumbnail = (videoData.thumbnail) && this.getMediaOriginURL(videoData.thumbnail); + this.showAssetPicker = false; + this.videoDataOutput.emit(videoData); + if (videoModal) { + videoModal.deny(); + } + } + } + + getMediaOriginURL(src) { + const replaceText = this.assetProxyUrl; + const cloudStorageUrls = _.compact(_.get(this.editorService.editorConfig, 'context.cloudStorageUrls') || []); + _.forEach(cloudStorageUrls, url => { + if (src.indexOf(url) !== -1) { + src = src.replace(url, replaceText); + } + }); + return src; + } + + /** + * function to lazy load all images + */ + lazyloadAllAssets() { + const offset = this.allAssets.length; + this.getAllAssets(offset, this.query, true); + } + + openAssetUploadModal() { + this.showAssetUploadModal = true; + this.resetFormData(); + } + + resetFormData() { + this.showErrorMsg = false; + this.formData = null; + this.formConfig = this.initialFormConfig; + this.assetUploadLoader = false; + this.assetFormValid = false; + this.loading = false; + this.isClosable = true; + } + + dismissAssetUploadModal() { + if (this.isClosable) { + this.showAssetUploadModal = false; + } + } + + initiateAssetUploadModal() { + this.showAssetPicker = false; + this.showAssetUploadModal = true; + this.loading = false; + this.isClosable = true; + } + + public isEditorReadOnly(state) { + this.editorInstance.isReadOnly = state; + this.isAssetBrowserReadOnly = state; + } + + uploadAsset(event) { + const file = event.target.files[0]; + this.assetName = file.name; + const reader = new FileReader(); + this.formData = new FormData(); + this.formData.append('file', file); + const fileType = file.type; + const fileName = file.name.split('.').slice(0, -1).join('.'); + const fileSize = file.size / 1024 / 1024; + if (fileType.split('/')[0] === this.assetType) { + this.showErrorMsg = false; + if (fileSize > this.assetConfig[this.assetType].size) { + this.showErrorMsg = true; + this.errorMsg = _.get(this.configService.labelConfig, 'messages.error.021') + + this.assetConfig[this.assetType].size + this.assetConfig[this.assetType].sizeType; + this.resetFormConfig(); + } else { + this.errorMsg = ''; + this.showErrorMsg = false; + reader.readAsDataURL(file); + } + } else { + this.showErrorMsg = true; + this.errorMsg = _.get(this.configService.labelConfig?.chooseFileMsg[this.assetType]); + } if (!this.showErrorMsg) { + this.assetUploadLoader = true; + this.assetFormValid = true; + this.assetData = this.generateAssetCreateRequest(fileName, fileType, this.assetType); + this.populateFormData(this.assetData); + } + } + + generateAssetCreateRequest(fileName, fileType, mediaType) { + return { + name: fileName, + mediaType, + mimeType: fileType, + createdBy: _.get(this.editorService.editorConfig, 'context.user.id'), + creator: _.get(this.editorService.editorConfig, 'context.user.fullName'), + channel: _.get(this.editorService.editorConfig, 'context.channel') + }; + } + + populateFormData(formData) { + const formvalue = _.cloneDeep(this.formConfig); + this.formConfig = null; + _.forEach(formvalue, (formFieldCategory) => { + formFieldCategory.default = formData[formFieldCategory.code]; + }); + this.formConfig = formvalue; + } + + resetFormConfig() { + this.assetUploadLoader = false; + this.assetFormValid = false; + this.formConfig = this.initialFormConfig; + } + + onStatusChanges(event) { + if (event.isValid && this.assetUploadLoader) { + this.assetFormValid = true; + } else { + this.assetFormValid = false; + } + } + + valueChanges(event) { + this.assetData = _.merge({}, this.assetData, event); + } + + dismissPops(modal) { + this.dismissAssetPicker(); + modal.deny(); + } + + uploadAndUseAsset(modal) { + this.isClosable = false; + this.loading = true; + this.showErrorMsg = false; + this.assetFormValid = false; + if (!this.showErrorMsg) { + this.questionService.createMediaAsset({ asset: this.assetData }).pipe(catchError(err => { + this.loading = false; + this.isClosable = true; + this.assetFormValid = true; + let errInfo; + if(this.assetType === 'video') { + errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.025') }; + } else if(this.assetType === 'image') { + errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.019') }; + } + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + })).subscribe((res) => { + const contentId = res.result.node_id; + const request = { + content: { + fileName: this.assetName + } + }; + this.questionService.generatePreSignedUrl(request, contentId).pipe(catchError(err => { + let errInfo; + errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.026') }; + this.loading = false; + this.isClosable = true; + this.assetFormValid = true; + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + })).subscribe((response) => { + const signedURL = response.result.pre_signed_url; + let blobConfig = { + processData: false, + contentType: 'Asset' + }; + blobConfig = this.editorService.appendCloudStorageHeaders(blobConfig); + this.uploadToBlob(signedURL, this.assetFile, blobConfig).subscribe(() => { + const fileURL = signedURL.split('?')[0]; + if (this.assetType === 'video') { + this.updateContentWithURL(fileURL, this.assetFile.type, contentId, modal); + } else if (this.assetType ==='image') { + const data = new FormData(); + data.append('fileUrl', fileURL); + data.append('mimeType', _.get(this.assetFile, 'type')); + const config1 = { + enctype: 'multipart/form-data', + processData: false, + contentType: false, + cache: false + }; + const uploadMediaConfig = { + data, + param: config1 + }; + this.questionService.uploadMedia(uploadMediaConfig, contentId).pipe(catchError(err => { + const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.019') }; + this.isClosable = true; + this.loading = false; + this.assetFormValid = true; + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + })).subscribe((res) => { + this.addAssetInEditor(res.result.content_url, res.result.node_id); + this.showAssetUploadModal = false; + this.dismissPops(modal); + }) + } + }) + }) + }) + } + } + + updateContentWithURL(fileURL, mimeType, contentId, modal?) { + const data = new FormData(); + data.append('fileUrl', fileURL); + data.append('mimeType', mimeType); + const conf = { + enctype: 'multipart/form-data', + processData: false, + contentType: false, + cache: false + }; + const option = { + data, + param: config + }; + this.questionService.uploadMedia(option, contentId).pipe(catchError(err => { + const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.027') }; + this.isClosable = true; + this.loading = false; + this.assetFormValid = true; + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + })).subscribe(res => { + // Read upload asset data + this.getUploadAsset(res.result.node_id, modal); + }); + } + + getUploadAsset(assetId, modal?) { + this.questionService.getVideo(assetId).pipe(map((data: any) => data.result.content), catchError(err => { + const errInfo = { errorMsg: _.get(this.configService, 'labelConfig.messages.error.011') }; + this.loading = false; + this.isClosable = true; + this.assetFormValid = true; + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + })).subscribe(res => { + this.toasterService.success(_.get(this.configService, 'labelConfig.messages.success.006')); + this.selectedAsset = res; + this.showAddButton = true; + this.loading = false; + this.isClosable = true; + this.assetFormValid = true; + this.addAssetInEditor(modal); + }); + } + + uploadToBlob(signedURL, file, config): Observable { + return this.questionService.http.put(signedURL, file, config).pipe(catchError(err => { + const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; + this.isClosable = true; + this.loading = false; + this.assetFormValid = true; + return throwError(this.editorService.apiErrorHandling(err, errInfo)); + }), map(data => data)); + } + + ngOnDestroy() { + if (this.modal && this.modal.deny) { + this.modal.deny(); + } + } +} \ No newline at end of file diff --git a/projects/questionset-editor-library/src/lib/questionset-editor-library.module.ts b/projects/questionset-editor-library/src/lib/questionset-editor-library.module.ts index e2f5303eb..b77cdc461 100644 --- a/projects/questionset-editor-library/src/lib/questionset-editor-library.module.ts +++ b/projects/questionset-editor-library/src/lib/questionset-editor-library.module.ts @@ -38,6 +38,7 @@ import { ProgressStatusComponent } from './components/progress-status/progress-s import {TermAndConditionComponent} from './components/term-and-condition/term-and-condition.component'; import { QualityParamsModalComponent } from './components/quality-params-modal/quality-params-modal.component'; +import { AssetsBrowserComponent } from './components/assets-browser/assets-browser.component'; @NgModule({ declarations: [ QuestionsetEditorLibraryComponent, @@ -68,7 +69,8 @@ import { QualityParamsModalComponent } from './components/quality-params-modal/q PlainTreeComponent, ProgressStatusComponent, TermAndConditionComponent, - QualityParamsModalComponent + QualityParamsModalComponent, + AssetsBrowserComponent, ], imports: [CommonModule, FormsModule, ReactiveFormsModule.withConfig({callSetDisabledState: 'whenDisabledForLegacyCode'}), RouterModule.forChild([]), SuiModule, CommonFormElementsModule, InfiniteScrollModule, HttpClientModule, ResourceLibraryModule, A11yModule], diff --git a/projects/questionset-editor-library/src/lib/services/config/label.config.json b/projects/questionset-editor-library/src/lib/services/config/label.config.json index 29e13cd2d..5eb298dfb 100644 --- a/projects/questionset-editor-library/src/lib/services/config/label.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/label.config.json @@ -77,7 +77,7 @@ "answers":"Answers", "answersRequired":"Answer is required", "answersPopupText":"Please provide an answer for the question. Check preview to understand how it would look.", - "selectImage":"Select Image", + "selectImage":"Select Image", "myImages":"My Images", "allImage":"All Image", "uploadAndUse":"Upload and Use", @@ -91,9 +91,28 @@ "copyRightsAndLicense":"Copyright & License", "dropChooseFile":"Drop or choose file to upload before entering the details", "charactersLeft":"Characters left:", + "myAssets": { + "image": "My Images", + "video": "My Video(s)" + }, + "allAssets": { + "image": "All Image", + "video": "All Video(s)" + }, + "selectAsset": { + "image":"Select Image", + "video":"Select Video" + }, + "chooseOrDragAsset": { + "image": "Choose or drag and drop your image here", + "video": "Choose or drag and drop your video here" + }, "myVideos":"My Video(s)", "allVideos":"All Video(s)", "selectVideo":"Select Video", + "myAudios": "My Audio(s)", + "allAudios":"All Audio(s)", + "selectAudio":"Select Audio", "searchPlaceholder":"Search...", "addAnImage":"Add an image", "name":"Name", @@ -247,6 +266,14 @@ "termsAndConditions": { "001": "I understand and confirm that all resources and assets created through the content editor or uploaded on the platform shall be available for free and public use without limitations on the platform (web portal, applications and any other end user interface that the platform would enable) and will be licensed under terms & conditions and policy guidelines of the platform. In doing so, the copyright and license of the original author is not infringed." }, + "emptySearchMessage": { + "image": "No images found, please try searching for something else", + "video": "No videos found, please try searching for something else" + }, + "chooseFileMsg": { + "image": "Please choose an image file", + "video": "Please choose a video file" + }, "messages": { "error": { "001": "Something went wrong, Please try later", From fd1f2c7f4ec6ffa5bd84a3bb50096e2670fad110 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Mon, 24 Jul 2023 19:21:52 +0530 Subject: [PATCH 02/18] feat(AudioUpload): Audio Upload Implementation --- .../assets-browser.component.html | 4 +- .../assets-browser.component.ts | 73 +++++------- .../fancy-tree.component.spec.data.ts | 4 + .../question/question.component.html | 51 +++++++-- .../components/question/question.component.ts | 107 ++++++++++-------- .../lib/services/config/editor.config.json | 5 + .../src/lib/services/config/label.config.json | 29 ++++- 7 files changed, 170 insertions(+), 103 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html index 684758802..65d51795b 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html @@ -13,9 +13,9 @@ diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index 65a23c298..0baa1deb9 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -17,8 +17,9 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { @ViewChild('editor') public editorRef: ElementRef; @Output() videoDataOutput = new EventEmitter(); @Output() editorDataOutput = new EventEmitter(); + @Output() assetDataOutput = new EventEmitter(); @Input() editorDataInput: any; - @Input() videoShow; + @Input() assetShow; @Input() assetType; @Input() showAssetPicker; @Output() assetBrowserEmitter = new EventEmitter(); @@ -147,12 +148,12 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } ngOnChanges() { - if (this.videoShow) { + if (this.assetShow) { this.showAssetPicker = true; this.selectAsset(undefined); } } - + getAcceptType(typeList, type) { const acceptTypeList = typeList.split(', '); const result = []; @@ -164,13 +165,11 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { dismissAssetPicker() { this.showAssetPicker = false; - if(this.assetType=='video') { - this.videoShow=false; - this.videoDataOutput.emit(false); - } else if(this.assetType == 'image') { - this.showAssetPicker = false; - this.modalDismissEmitter.emit({}); - } + this.assetShow=false; + this.videoDataOutput.emit(false); + this.showAssetPicker = false; + this.modalDismissEmitter.emit({}); + this.assetDataOutput.emit(false); } selectAsset(data) { @@ -189,7 +188,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { this.assetsCount = 0; if (!search) { this.searchMyInput = ''; - if(this.assetType == 'video') { + if(this.assetType != 'image' ) { this.selectAsset(undefined); } } @@ -209,11 +208,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } this.questionService.getAssetMedia(req).pipe(catchError(err => { let errInfo; - if (this.assetType == 'image') { - errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.022') }; - } else if (this.assetType == 'video') { - errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.023')}; - } + errInfo = _.get(this.configService.labelConfig?.assetSearchFailed[this.assetType]); return throwError(this.editorService.apiErrorHandling(err, errInfo)); })).subscribe((res) => { this.assetsCount = res.result.count; @@ -245,11 +240,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } this.questionService.getAssetMedia(req).pipe(catchError(err => { let errInfo; - if (this.assetType == 'image') { - errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.022') }; - } else if (this.assetType == 'video') { - errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.023') }; - } + errInfo = _.get(this.configService.labelConfig?.assetSearchFailed[this.assetType]); return throwError(this.editorService.apiErrorHandling(err, errInfo)); })).subscribe((res) => { this.assetsCount = res.result.count; @@ -307,16 +298,16 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { }); this.showAssetPicker = false; this.showAssetUploadModal = false; - } else if (this.assetType == 'video') { - const videoData: any = _.cloneDeep(this.selectedAsset); - videoData.src = this.getMediaOriginURL(videoData.downloadUrl); - videoData.thumbnail = (videoData.thumbnail) && this.getMediaOriginURL(videoData.thumbnail); + } else { + const assetData: any = _.cloneDeep(this.selectedAsset); + assetData.src = this.getMediaOriginURL(assetData.downloadUrl); + assetData.thumbnail = (assetData.thumbnail) && this.getMediaOriginURL(assetData.thumbnail); this.showAssetPicker = false; - this.videoDataOutput.emit(videoData); + this.assetDataOutput.emit(assetData); if (videoModal) { videoModal.deny(); } - } + } } getMediaOriginURL(src) { @@ -372,14 +363,14 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } uploadAsset(event) { - const file = event.target.files[0]; - this.assetName = file.name; + this.assetFile = event.target.files[0]; + this.assetName = this.assetFile.name; const reader = new FileReader(); this.formData = new FormData(); - this.formData.append('file', file); - const fileType = file.type; - const fileName = file.name.split('.').slice(0, -1).join('.'); - const fileSize = file.size / 1024 / 1024; + this.formData.append('file', this.assetFile); + const fileType = this.assetFile.type; + const fileName = this.assetFile.name.split('.').slice(0, -1).join('.'); + const fileSize = this.assetFile.size / 1024 / 1024; if (fileType.split('/')[0] === this.assetType) { this.showErrorMsg = false; if (fileSize > this.assetConfig[this.assetType].size) { @@ -390,7 +381,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } else { this.errorMsg = ''; this.showErrorMsg = false; - reader.readAsDataURL(file); + reader.readAsDataURL(this.assetFile); } } else { this.showErrorMsg = true; @@ -457,11 +448,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { this.isClosable = true; this.assetFormValid = true; let errInfo; - if(this.assetType === 'video') { - errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.025') }; - } else if(this.assetType === 'image') { - errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.019') }; - } + errInfo = { errorMsg: _.get(this.configService.labelConfig?.chooseFileMsg[this.assetType]) }; return throwError(this.editorService.apiErrorHandling(err, errInfo)); })).subscribe((res) => { const contentId = res.result.node_id; @@ -486,9 +473,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { blobConfig = this.editorService.appendCloudStorageHeaders(blobConfig); this.uploadToBlob(signedURL, this.assetFile, blobConfig).subscribe(() => { const fileURL = signedURL.split('?')[0]; - if (this.assetType === 'video') { - this.updateContentWithURL(fileURL, this.assetFile.type, contentId, modal); - } else if (this.assetType ==='image') { + if (this.assetType ==='image') { const data = new FormData(); data.append('fileUrl', fileURL); data.append('mimeType', _.get(this.assetFile, 'type')); @@ -513,6 +498,8 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { this.showAssetUploadModal = false; this.dismissPops(modal); }) + } else { + this.updateContentWithURL(fileURL, this.assetFile.type, contentId, modal); } }) }) @@ -532,7 +519,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { }; const option = { data, - param: config + param: conf }; this.questionService.uploadMedia(option, contentId).pipe(catchError(err => { const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.027') }; diff --git a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts index 89bee6110..4a9aaf33f 100644 --- a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts @@ -559,6 +559,10 @@ export const editorConfig = { size: "50", accepted: "mp4, webm", }, + audio: { + size: "50", + accepted: "mp3" + } }, mode: "edit", maxDepth: 2, diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.html b/projects/questionset-editor-library/src/lib/components/question/question.component.html index 5a26f2329..75730054a 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.html +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.html @@ -34,10 +34,18 @@
+ (videoDataOutput)="assetDataOutput($event)" [videoShow]="assetShow" + > +
@@ -89,6 +97,10 @@ [telemetryInteractEdata]="telemetryService.getTelemetryInteractEdata('solution_type','select','single_select', telemetryService.telemetryPageId, {solution_type:'video'})"> {{configService.labelConfig?.lbl?.video}}
+
+ {{configService.labelConfig?.lbl?.audio}} +
@@ -106,6 +118,31 @@ +
+
+
+
+ +
+
+
+ +
+
+
+ {{ assetSolutionName }} +
+
+
+
@@ -118,15 +155,15 @@
- {{ videoSolutionName + {{ assetSolutionName }}
@@ -146,10 +183,10 @@
-
+
- {{videoSolutionName}} + {{assetSolutionName}}
diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.ts index cb1e50d5d..a37268c8c 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.ts @@ -38,20 +38,22 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { @Output() questionEmitter = new EventEmitter(); private onComponentDestroy$ = new Subject(); toolbarConfig: any = {}; + public showAssetPicker = false; public terms = false; public editorState: any = {}; public showPreview = false; public mediaArr: any = []; - public videoShow = false; + public assetShow = false; public showFormError = false; public actionType: string; + assetType: string; selectedSolutionType: string; selectedSolutionTypeIndex: string; showSolutionDropDown = true; showSolution = false; - videoSolutionName: string; - videoSolutionData: any; - videoThumbnail: string; + assetSolutionName: string; + assetSolutionData: any; + assetThumbnail: string; solutionUUID: string; solutionValue: string; solutionTypes: any = [{ @@ -61,6 +63,10 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { { type: 'video', value: 'video' + }, + { + type: 'audio', + value: 'audio' }]; questionMetaData: any; questionInteractionType; @@ -252,13 +258,15 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { this.solutionUUID = this.editorState.solutions[0].id; this.showSolutionDropDown = false; this.showSolution = true; - if (this.selectedSolutionType === 'video') { + console.log(this.selectedSolutionType); + if (this.selectedSolutionType === 'video' || this.selectedSolutionType === 'audio') { const index = _.findIndex(this.questionMetaData.media, (o) => { - return o.type === 'video' && o.id === this.editorState.solutions[0].value; + return o.type === this.selectedSolutionType && o.id === this.editorState.solutions[0].value; }); - this.videoSolutionName = this.questionMetaData.media[index].name; - this.videoThumbnail = this.questionMetaData.media[index].thumbnail; - } + console.log(this.questionMetaData.media[index]); + this.assetSolutionName = this.questionMetaData.media[index].name; + this.assetThumbnail = this.questionMetaData.media[index].thumbnail; + } if (this.selectedSolutionType === 'html') { this.editorState.solutions = this.editorState.solutions[0].value; } @@ -683,59 +691,63 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { } } - videoDataOutput(event) { + assetDataOutput(event) { + console.log("check"); if (event) { - this.videoSolutionData = event; - this.videoSolutionName = event.name; + console.log(event); + this.assetSolutionData = event; + this.assetSolutionName = event.name; this.editorState.solutions = event.identifier; - this.videoThumbnail = event.thumbnail; - const videoMedia: any = {}; - videoMedia.id = event.identifier; - videoMedia.src = event.src; - videoMedia.type = 'video'; - videoMedia.assetId = event.identifier; - videoMedia.name = event.name; - videoMedia.thumbnail = this.videoThumbnail; - videoMedia.baseUrl = _.get(this.editorService.editorConfig, 'context.host') || document.location.origin; - if (videoMedia.thumbnail) { + this.assetThumbnail = event.thumbnail; + const assetMedia: any = {}; + assetMedia.id = event.identifier; + assetMedia.src = event.src; + assetMedia.type = this.assetType; + assetMedia.assetId = event.identifier; + assetMedia.name = event.name; + assetMedia.thumbnail = this.assetThumbnail; + assetMedia.baseUrl = _.get(this.editorService.editorConfig, 'context.host') || document.location.origin; + if (assetMedia.thumbnail) { const thumbnailMedia: any = {}; - thumbnailMedia.src = this.videoThumbnail; + thumbnailMedia.src = this.assetThumbnail; thumbnailMedia.type = 'image'; - thumbnailMedia.id = `video_${event.identifier}`; + thumbnailMedia.id = `${this.assetType}_${event.identifier}`; thumbnailMedia.baseUrl = _.get(this.editorService.editorConfig, 'context.host') || document.location.origin; this.mediaArr.push(thumbnailMedia); } - this.mediaArr.push(videoMedia); + this.mediaArr.push(assetMedia); this.showSolutionDropDown = false; this.showSolution = true; } else { this.deleteSolution(); } - this.videoShow = false; + this.assetShow = false; } selectSolutionType(data: any) { const index = _.findIndex(this.solutionTypes, (sol: any) => { return sol.value === data; }); + this.assetType = data; this.selectedSolutionType = this.solutionTypes[index].type; - if (this.selectedSolutionType === 'video') { - const showVideo = true; - this.videoShow = showVideo; - } else { + if (this.selectedSolutionType === 'video' || this.selectedSolutionType === 'audio') { + const showAsset = true; + this.assetShow = showAsset; + } + else { this.showSolutionDropDown = false; } } deleteSolution() { - if (this.selectedSolutionType === 'video') { + if (this.selectedSolutionType === 'video' || this.selectedSolutionType === 'audio') { this.mediaArr = _.filter(this.mediaArr, (item: any) => item.id !== this.editorState.solutions); - } + } this.showSolutionDropDown = true; this.selectedSolutionType = ''; - this.videoSolutionName = ''; + this.assetSolutionName = ''; this.editorState.solutions = ''; - this.videoThumbnail = ''; + this.assetThumbnail = ''; this.showSolution = false; } @@ -843,12 +855,12 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { getQuestionSolution(solutionObj) { if (solutionObj?.type === 'html') { return {[solutionObj?.id]: solutionObj.value}; - } else if (solutionObj?.type === 'video') { - const videoMedia = this.getMediaById(solutionObj?.value); - const videoThumbnail = videoMedia?.thumbnail ? videoMedia?.thumbnail : ''; - const videoSolution = this.getVideoSolutionHtml(videoThumbnail, videoMedia?.src, videoMedia.id); - return {[solutionObj.id]: videoSolution}; - } + } else if (solutionObj?.type === 'video' || solutionObj?.type === 'audio') { + const assetMedia = this.getMediaById(solutionObj?.value); + const assetThumbnail = assetMedia?.thumbnail ? assetMedia?.thumbnail : ''; + const assetSolution = this.getAssetSolutionHtml(assetThumbnail, assetMedia?.src, assetMedia.id); + return {[solutionObj.id]: assetSolution}; + } } getMediaById(mediaId) { @@ -864,13 +876,18 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { return responseDeclaration; } - getVideoSolutionHtml(posterURL, srcUrl, solutionMediaId) { - const videoSolutionHtml = '' - const videoSolutionValue = videoSolutionHtml.replace('{posterUrl}', posterURL).replace('{sourceURL}', srcUrl).replace('{sourceURL}', srcUrl).replace('{solutionMediaId}', solutionMediaId); - return videoSolutionValue; + getAssetSolutionHtml(posterURL, srcUrl, solutionMediaId) { + let assetSolutionHtml + if (this.assetType === 'video') { + assetSolutionHtml = '' + } else if(this.assetType === 'audio') { + assetSolutionHtml = '' + } + const assetSolutionValue = assetSolutionHtml.replace('{posterUrl}', posterURL).replace('{sourceURL}', srcUrl).replace('{sourceURL}', srcUrl).replace('{solutionMediaId}', solutionMediaId); + return assetSolutionValue; } - + getMcqQuestionHtmlBody(question, templateId) { const mcqTemplateConfig = { // tslint:disable-next-line:max-line-length diff --git a/projects/questionset-editor-library/src/lib/services/config/editor.config.json b/projects/questionset-editor-library/src/lib/services/config/editor.config.json index 195ea0ad1..3e4ffeaf8 100644 --- a/projects/questionset-editor-library/src/lib/services/config/editor.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/editor.config.json @@ -15,6 +15,11 @@ "size": "50", "sizeType": "MB", "accepted": "mp4, webm" + }, + "audio": { + "size": "50", + "sizeType": "MB", + "accepted": "mp3" } }, "questionPrimaryCategories": ["Multiple Choice Question", "Subjective Question"], diff --git a/projects/questionset-editor-library/src/lib/services/config/label.config.json b/projects/questionset-editor-library/src/lib/services/config/label.config.json index 5eb298dfb..d909285ed 100644 --- a/projects/questionset-editor-library/src/lib/services/config/label.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/label.config.json @@ -93,19 +93,23 @@ "charactersLeft":"Characters left:", "myAssets": { "image": "My Images", - "video": "My Video(s)" + "video": "My Video(s)", + "audio": "My Audio" }, "allAssets": { "image": "All Image", - "video": "All Video(s)" + "video": "All Video(s)", + "audio": "All Audio" }, "selectAsset": { "image":"Select Image", - "video":"Select Video" + "video":"Select Video", + "audio":"Select Audio" }, "chooseOrDragAsset": { "image": "Choose or drag and drop your image here", - "video": "Choose or drag and drop your video here" + "video": "Choose or drag and drop your video here", + "audio": "Choose or drag and drop your audio here" }, "myVideos":"My Video(s)", "allVideos":"All Video(s)", @@ -157,6 +161,7 @@ "pageNumber":"Page No", "confirmQuestionNotSaved":"This question will not be saved, are you sure you want to go back to questionset?", "video":"Video", + "audio":"Audio", "textImage":"Text+Image", "chooseType":"Choose type", "solution":"Solution", @@ -268,11 +273,23 @@ }, "emptySearchMessage": { "image": "No images found, please try searching for something else", - "video": "No videos found, please try searching for something else" + "video": "No videos found, please try searching for something else", + "audio": "No audios found, please try searching for something else" }, "chooseFileMsg": { "image": "Please choose an image file", - "video": "Please choose a video file" + "video": "Please choose a video file", + "audio": "Please choose a audio file" + }, + "assetSearchFailed": { + "image": "Image search failed", + "video": "Video search failed", + "audio": "Audio search falied" + }, + "assetUploadFailed": { + "image": "Image upload failed", + "audio": "Video upload failed", + "video": "Audio upload failed" }, "messages": { "error": { From 55532002ba4de8a5f978716dc7207248c752bba1 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Sat, 12 Aug 2023 14:28:33 +0530 Subject: [PATCH 03/18] feat(audioUpload): Integration of video with assetBrowser --- package.json | 3 + .../assets-browser.component.html | 20 ++++++ .../assets-browser.component.ts | 72 +++++++++++++++++-- .../editor/editor.component.spec.data.ts | 1 + .../fancy-tree.component.spec.data.ts | 2 +- .../question/question.component.html | 25 ++++--- .../question/question.component.spec.ts | 30 ++++---- .../components/question/question.component.ts | 15 ++-- .../translations/translations.component.ts | 2 +- .../lib/services/config/editor.config.json | 2 +- 10 files changed, 126 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index d9b821dfe..b6cf89cdc 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "jquery": "^3.5.1", "jquery.fancytree": "^2.37.0", "karma-mocha-reporter": "^2.2.5", + "katex": "^0.11.1", "lodash-es": "^4.17.21", "mathjax-full": "^3.1.2", "moment": "^2.29.1", @@ -56,6 +57,8 @@ "ngx-bootstrap": "^10.0.0", "ngx-chips": "^2.2.2", "ngx-infinite-scroll": "^14.0.0", + "recorder-js": "^1.0.7", + "recordrtc": "^5.6.2", "rxjs": "~6.6.3", "svg2img": "^0.6.1", "tslib": "^2.0.0", diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html index 65d51795b..3031a03da 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html @@ -87,6 +87,26 @@
{{chooseOrDragAst}}*
+ +
  • {{configService.labelConfig?.lbl?.allowedFileTypes}} {{acceptedFileType}}
  • diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index 0baa1deb9..bb19d72fc 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -7,7 +7,8 @@ import { ConfigService } from '../../services/config/config.service'; import { QuestionService } from '../../services/question/question.service'; import { config } from 'projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.data'; import { ToasterService } from '../../services/toaster/toaster.service'; - +import * as RecordRTC from 'recordrtc'; +import { DomSanitizer } from '@angular/platform-browser'; @Component({ selector: 'lib-assets-browser', templateUrl: './assets-browser.component.html', @@ -48,7 +49,10 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { astSize: string; astSizeType: string; acceptedFileType: string; - + record: any; + url: any; + fileType: any; + recording = false; public assetData = {}; public assetFile: any; public formData: any; @@ -66,7 +70,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { public searchAllInput: any; public assetUploadLoader = false; constructor(private editorService: EditorService, public configService: ConfigService, - private questionService: QuestionService, public toasterService: ToasterService) { } + private questionService: QuestionService, public toasterService: ToasterService, private domSanitizer: DomSanitizer) { } ngOnInit() { this.assetProxyUrl = _.get(this.editorService.editorConfig, 'config.assetProxyUrl') || _.get(this.configService.urlConFig, 'URLS.assetProxyUrl'); @@ -147,6 +151,60 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { }; } + // Record and Upload + sanitize(url: string) { + return this.domSanitizer.bypassSecurityTrustUrl(url); + } + + startRecording() { + this.recording = true; + let mediaConstraints = { + video: false, + audio: true, + }; + navigator.mediaDevices.getUserMedia(mediaConstraints).then(this.successCallback.bind(this), this.errorCallBack.bind(this)); + } + + successCallback(stream) { + var options = { + mimeType : 'audio/wav', + }; + var stereoAudioRecorder = RecordRTC.StereoAudioRecorder; + this.record = new stereoAudioRecorder(stream, options); + this.record.record(); + } + + stopRecording() { + this.recording = false; + this.record.stop(this.processRecording.bind(this)); + this.assetUploadLoader = true; + this.assetFormValid = true; + } + + processRecording(blob) { + this.url = URL.createObjectURL(blob); + // console.log(this.url); + const fileName = "recoding.wav"; + const fileType = blob['type']; + this.fileType = fileType; + this.assetType = "audio" + const reader = new FileReader(); + this.formData = new FormData(); + this.errorMsg = ''; + this.showErrorMsg = false; + reader.readAsDataURL(blob); + this.formData.append('file', blob); + this.assetUploadLoader = true; + this.assetFormValid = true; + this.assetData = this.generateAssetCreateRequest(fileName, fileType, this.assetType); + this.populateFormData(this.assetData); + this.assetName = fileName; + } + + errorCallBack() { + const error = "Unable to play audio in your browser"; + } + ngOnChanges() { if (this.assetShow) { this.showAssetPicker = true; @@ -499,7 +557,13 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { this.dismissPops(modal); }) } else { - this.updateContentWithURL(fileURL, this.assetFile.type, contentId, modal); + let fileType; + if (this.assetFile!==undefined) { + fileType = this.assetFile.type; + } else { + fileType = this.fileType; + } + this.updateContentWithURL(fileURL, fileType, contentId, modal); } }) }) diff --git a/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts index d54ed1102..b66697b1f 100644 --- a/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts @@ -717,6 +717,7 @@ export const categoryDefinition = { label: 'image/png', }, { value: 'audio/mp3', label: 'audio/mp3' }, + { value: 'audio/wav', label: 'audio/wav' }, { value: 'video/mp4', label: 'video/mp4' }, { value: 'video/webm', label: 'video/webm' }, ], diff --git a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts index 4a9aaf33f..f42539831 100644 --- a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts @@ -561,7 +561,7 @@ export const editorConfig = { }, audio: { size: "50", - accepted: "mp3" + accepted: "mp3, wav" } }, mode: "edit", diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.html b/projects/questionset-editor-library/src/lib/components/question/question.component.html index 75730054a..2f83b784d 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.html +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.html @@ -34,13 +34,12 @@
    + *ngIf="!isReadOnlyMode" + (editorDataOutput)="editorDataHandler($event, 'question')" + [editorDataInput]="editorState.question" + >
    -
    +
    @@ -143,7 +142,7 @@
    -
    + -
    +
    @@ -180,7 +179,7 @@

    -
    +
    diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts index 84b477943..69836993a 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts @@ -747,7 +747,7 @@ describe("QuestionComponent", () => { component.mediaArr = mediaVideoArray; spyOn(component, 'getQuestionSolution').and.callThrough(); spyOn(component, 'getMediaById').and.callThrough(); - spyOn(component, 'getVideoSolutionHtml').and.callThrough(); + spyOn(component, 'getAssetSolutionHtml').and.callThrough(); const solution = component.getQuestionSolution(videoSolutionObject); expect(solution).toBeDefined(); }) @@ -770,9 +770,9 @@ describe("QuestionComponent", () => { expect(mediaobj).toBeDefined(); }); - it('#getVideoSolutionHtml() should return videoSolutionHtml', () => { - spyOn(component, 'getVideoSolutionHtml').and.callThrough(); - const videoSolutionHtml = component.getVideoSolutionHtml(mediaVideoArray[0].thubmnail, mediaVideoArray[0].src, mediaVideoArray[0].id); + it('#getAssetSolutionHtml() should return videoSolutionHtml', () => { + spyOn(component, 'getAssetSolutionHtml').and.callThrough(); + const videoSolutionHtml = component.getAssetSolutionHtml(mediaVideoArray[0].thubmnail, mediaVideoArray[0].src, mediaVideoArray[0].id); expect(videoSolutionHtml).toBeDefined(); }); @@ -1550,34 +1550,34 @@ describe("QuestionComponent", () => { component.addResourceToQuestionset(); }); - it("#videoDataOutput() should call videoDataOutput and event data is empty", () => { + it("#assetDataOutput() should call assetDataOutput and event data is empty", () => { const event = ""; spyOn(component, "deleteSolution"); - component.videoDataOutput(event); + component.assetDataOutput(event); expect(component.deleteSolution).toHaveBeenCalled(); }); - it("#videoDataOutput() should call videoDataOutput and event data is not empty", () => { + it("#assetDataOutput() should call assetDataOutput and event data is not empty", () => { const event = { name: "event name", identifier: "1234" }; - component.videoDataOutput(event); - expect(component.videoSolutionData).toBeDefined(); + component.assetDataOutput(event); + expect(component.assetSolutionData).toBeDefined(); }); - it("#videoDataOutput() should call videoDataOutput for thumbnail", () => { + it("#assetDataOutput() should call assetDataOutput for thumbnail", () => { const event = { name: "event name", identifier: "1234", thumbnail: "sample data", }; - component.videoDataOutput(event); - expect(component.videoSolutionData).toBeDefined(); + component.assetDataOutput(event); + expect(component.assetSolutionData).toBeDefined(); }); - it("#videoDataOutput() should call videoDataOutput for thumbnail", () => { + it("#assetDataOutput() should call assetDataOutput for thumbnail", () => { const event = { name: "event name", identifier: "1234", thumbnail: "sample data", }; - component.videoDataOutput(event); - expect(component.videoSolutionData).toBeDefined(); + component.assetDataOutput(event); + expect(component.assetSolutionData).toBeDefined(); }); it("#subMenuChange() should set the sub-menu value ", () => { spyOn(component,'subMenuChange').and.callThrough(); diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.ts index a37268c8c..9f7abf687 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.ts @@ -258,12 +258,10 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { this.solutionUUID = this.editorState.solutions[0].id; this.showSolutionDropDown = false; this.showSolution = true; - console.log(this.selectedSolutionType); if (this.selectedSolutionType === 'video' || this.selectedSolutionType === 'audio') { const index = _.findIndex(this.questionMetaData.media, (o) => { return o.type === this.selectedSolutionType && o.id === this.editorState.solutions[0].value; }); - console.log(this.questionMetaData.media[index]); this.assetSolutionName = this.questionMetaData.media[index].name; this.assetThumbnail = this.questionMetaData.media[index].thumbnail; } @@ -692,9 +690,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { } assetDataOutput(event) { - console.log("check"); if (event) { - console.log(event); this.assetSolutionData = event; this.assetSolutionName = event.name; this.editorState.solutions = event.identifier; @@ -745,6 +741,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { } this.showSolutionDropDown = true; this.selectedSolutionType = ''; + this.assetType = ''; this.assetSolutionName = ''; this.editorState.solutions = ''; this.assetThumbnail = ''; @@ -773,8 +770,6 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { media: this.mediaArr, editorState: {} }; - console.log('getQuestionMetadata'); - console.log(this.editorState); metadata = _.assign(metadata, this.editorState); metadata.editorState.question = metadata.question; metadata.body = metadata.question; @@ -878,16 +873,15 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { getAssetSolutionHtml(posterURL, srcUrl, solutionMediaId) { let assetSolutionHtml - if (this.assetType === 'video') { + if (this.selectedSolutionType === 'video') { assetSolutionHtml = '' - } else if(this.assetType === 'audio') { - assetSolutionHtml = '' + } else if(this.selectedSolutionType === 'audio') { + assetSolutionHtml = '' } const assetSolutionValue = assetSolutionHtml.replace('{posterUrl}', posterURL).replace('{sourceURL}', srcUrl).replace('{sourceURL}', srcUrl).replace('{solutionMediaId}', solutionMediaId); return assetSolutionValue; } - getMcqQuestionHtmlBody(question, templateId) { const mcqTemplateConfig = { // tslint:disable-next-line:max-line-length @@ -1259,7 +1253,6 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { output(event) { } onStatusChanges(event) { - console.log(event); if (_.has(event, 'isValid')) { this.questionMetadataFormStatus = event.isValid; } diff --git a/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts b/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts index 23de89e8b..b2280db2a 100644 --- a/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts +++ b/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts @@ -98,7 +98,7 @@ export class TranslationsComponent { showEvidence: "Yes/No", evidence: { required: "Yes/No", - mimeType: ["image/png", "audio/mp3", "video/mp4", "video/webm"], + mimeType: ["image/png", "audio/mp3", "audio/wav", "video/mp4", "video/webm"], minCount: 1, maxCount: 1, sizeLimit: "20480", diff --git a/projects/questionset-editor-library/src/lib/services/config/editor.config.json b/projects/questionset-editor-library/src/lib/services/config/editor.config.json index 3e4ffeaf8..660906e74 100644 --- a/projects/questionset-editor-library/src/lib/services/config/editor.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/editor.config.json @@ -19,7 +19,7 @@ "audio": { "size": "50", "sizeType": "MB", - "accepted": "mp3" + "accepted": "mp3, wav" } }, "questionPrimaryCategories": ["Multiple Choice Question", "Subjective Question"], From 34f1b323328836b9805bb15d0abc4685f1b4a84d Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Tue, 22 Aug 2023 18:45:20 +0530 Subject: [PATCH 04/18] audio upload feature: testcases --- .../assets-browser.component.spec.data.ts | 95 ++++ .../assets-browser.component.spec.ts | 487 +++++++++++++++++- .../assets-browser.component.ts | 28 +- .../assets-browser/assets-browser.data.ts | 53 ++ .../collection-icon.component.html | 7 + .../collection-icon.component.ts | 2 +- 6 files changed, 656 insertions(+), 16 deletions(-) create mode 100644 projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.data.ts create mode 100644 projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.data.ts diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.data.ts new file mode 100644 index 000000000..6c1eeaa24 --- /dev/null +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.data.ts @@ -0,0 +1,95 @@ +export const mockData = { + assetBrowserEvent: { + type: 'image', + url: 'apple.png' + }, + event: { + target: { + files: [{ + lastModified: 1602826982711, + lastModifiedDate: 'Fri Oct 16 2020 11:13:02 GMT+0530 (India Standard Time)', + name: "logo.png", + size: 63344, + type: "image/png", + webkitRelativePath: "" + }] + } + }, + formData: { + channel: "01307938306521497658", + createdBy: "5a587cc1-e018-4859-a0a8-e842650b9d64", + creator: "Vaibahv Bhuva", + mediaType: "image", + mimeType: "image/png", + keywords: undefined, + name: "logo" + }, + uploadIconFormConfig: + [{ + 'code': 'name', + 'dataType': 'text', + 'editable': true, + 'inputType': 'text', + 'label': 'Asset Caption', + 'name': 'Asset Caption', + 'placeholder': 'Enter asset caption', + 'renderingHints': { + 'class': 'sb-g-col-lg-2 required' + }, + 'required': true, + 'visible': true, + 'validations': [ + { + 'type': 'required', + 'message': 'Please enter asset caption' + } + ] + }, + { + 'code': 'keywords', + 'visible': true, + 'editable': true, + 'dataType': 'list', + 'name': 'Tags', + 'placeholder': 'Add tag', + 'renderingHints': { + 'class': 'sb-g-col-lg-2' + }, + 'description': '', + 'inputType': 'keywords', + 'label': 'Tags', + 'required': true, + 'validations': [] + }, + { + 'code': 'creator', + 'dataType': 'text', + 'editable': true, + 'inputType': 'text', + 'label': 'Creator', + 'name': 'Creator', + 'placeholder': 'Enter name', + 'renderingHints': { + 'class': 'sb-g-col-lg-2' + }, + 'required': true, + 'visible': true + }], + serverResponse: { + id: '', + params: { + resmsgid: '', + msgid: '', + err: '', + status: '', + errmsg: '' + }, + responseCode: '200', + result: { + }, + ts: '', + ver: '', + headers: {} + } + }; + \ No newline at end of file diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts index 11de36c04..907330b54 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -1,23 +1,496 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { QuestionService } from './../../services/question/question.service'; import { AssetsBrowserComponent } from './assets-browser.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { EditorService } from '../../services/editor/editor.service'; +import { of, throwError } from 'rxjs'; +import * as _ from 'lodash-es'; +import { mockData } from '../asset-browser/asset-browser.component.spec.data'; + +const mockEditorService = { + editorConfig: { + config: { + assetConfig: { + image: { + size: '1', + accepted: 'png, jpeg' + }, + video: { + size: '50', + accepted: 'mp4, webm' + }, + audio: { + size: '50', + accepted: 'mp3, wav' + } + } + }, + context: { + user: { + id: 123, + fullName: 'Ram Gopal' + }, + channel: 'sunbird' + } + }, + + appendCloudStorageHeaders: (config) => { + return {...config, headers: {'x-ms-blob-type': 'BlockBlob'}}; + } +}; describe('AssetsBrowserComponent', () => { let component: AssetsBrowserComponent; let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ AssetsBrowserComponent ] + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [InfiniteScrollModule, HttpClientTestingModule, FormsModule], + declarations: [AssetsBrowserComponent], + providers: [{ provide: EditorService, useValue: mockEditorService }, QuestionService], + schemas: [CUSTOM_ELEMENTS_SCHEMA] }) - .compileComponents(); + .compileComponents(); + })); + beforeEach(() => { fixture = TestBed.createComponent(AssetsBrowserComponent); component = fixture.componentInstance; fixture.detectChanges(); - }); + }) it('should create', () => { expect(component).toBeTruthy(); }); + + it('#ngOnInit() should call #getAcceptType()', () => { + spyOn(component, 'ngOnInit').and.callThrough(); + spyOn(component, 'getAcceptType').and.callFake(() => {return ''}); + component.ngOnInit(); + expect(component.getAcceptType).toHaveBeenCalledWith(mockEditorService.editorConfig.config.assetConfig.image.accepted, 'image'); + }); + + it("#getAcceptType should return accepted content types", () => { + const typeList = "png, jpeg"; + const type = "image"; + spyOn(component, 'getAcceptType').and.callThrough(); + const result = component.getAcceptType(typeList, type); + expect(result).toEqual("image/png, image/jpeg"); + }); + + it('#ngOnInit() should call #getAcceptType()', () => { + spyOn(component, 'ngOnInit').and.callThrough(); + spyOn(component, 'getAcceptType').and.callFake(() => {return ''}); + component.ngOnInit(); + expect(component.getAcceptType).toHaveBeenCalledWith(mockEditorService.editorConfig.config.assetConfig.video.accepted, 'video'); + }); + + it("#getAcceptType should return accepted content types", () => { + const typeList = "mp4, webm"; + const type = "video"; + spyOn(component, 'getAcceptType').and.callThrough(); + const result = component.getAcceptType(typeList, type); + expect(result).toEqual("video/mp4, video/webm"); + }); + + it('#initializeAssetPicker() should set showAssetPicker to true', () => { + spyOn(component, 'initializeAssetPicker').and.callThrough(); + component.initializeAssetPicker(); + expect(component.showAssetPicker).toBeTruthy(); + }); + + it('#outputEventHandler() should log event', () => { + spyOn(component, 'outputEventHandler').and.callThrough(); + component.outputEventHandler({}); + expect(component.outputEventHandler).toHaveBeenCalled(); + }); + + it('#getMyAssets() should return assets on API success', + async () => { + const response = mockData.serverResponse; + response.result = { + count: 1, + content: [{ + downloadUrl: '/test' + }] + } + + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService, 'getAssetMedia').and.returnValue(of(response)); + const offset = 0; + component.getMyAssets(offset); + expect(component.assetsCount).toEqual(1); + }); + + it('#addAssetInEditor() should set showAssetPicker to false', () => { + spyOn(component, 'addAssetInEditor').and.callThrough(); + component.addAssetInEditor(mockData.assetBrowserEvent.url, '12345'); + // expect(component.appIcon).toBe(mockData.assetBrowserEvent.url); + }); + + // it('#addAssetInEditor() should set appIcon value', () => { + // spyOn(component, 'addAssetInEditor').and.callThrough(); + // component.addAssetInEditor(mockData.assetBrowserEvent.url, '12345'); + // expect(component.appIcon).toBe(mockData.assetBrowserEvent.url); + // }); + + it('#addAssetInEditor() should emit proper event', () => { + spyOn(component, 'addAssetInEditor').and.callThrough(); + spyOn(component.assetBrowserEmitter, 'emit').and.callFake(() => {}); + component.addAssetInEditor(mockData.assetBrowserEvent.url, '12345'); + expect(component.assetBrowserEmitter.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + }); + + it('#getAllAssets() should return assets on API success', async () => { + const response = mockData.serverResponse; + response.result = { + count: 1, + content: [{ + downloadUrl: '/test' + }] + } + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService, 'getAssetMedia').and.returnValue(of(response)); + const offset = 0; + component.getAllAssets(offset); + spyOn(component.allAssets, 'push'); + expect(component.assetsCount).toEqual(1); + }); + + it('#resetFormData() should reset the form data', () => { + component.resetFormData(); + expect(component.assetUploadLoader).toEqual(false); + expect(component.assetFormValid).toEqual(false); + expect(component.formConfig).toBeTruthy(); + }) + + it('#uploadAndUseAsset should upload asset on API success', async () => { + const createMediaAssetResponse = mockData.serverResponse; + createMediaAssetResponse.result = { + node_id: 'do_123' + } + const preSignedResponse = mockData.serverResponse; + preSignedResponse.result = { + node_id: 'do_234', + pre_signed_url: '/test' + } + let questionService: QuestionService = TestBed.inject(QuestionService); + let modal = true; + spyOn(questionService, 'createMediaAsset').and.returnValue(of(createMediaAssetResponse)); + spyOn(questionService, 'generatePreSignedUrl').and.returnValue(of(preSignedResponse)); + const editorService = TestBed.inject(EditorService); + spyOn(editorService, 'appendCloudStorageHeaders').and.callThrough(); + spyOn(component, 'dismissPops').and.callThrough(); + component.uploadAndUseAsset(modal); + expect(questionService.createMediaAsset).toHaveBeenCalled(); + expect(component.loading).toEqual(true); + expect(component.isClosable).toEqual(false); + expect(component.assetFormValid).toEqual(false); + }); + + xit('#uploadAndUseAsset should upload asset and call upload to blob', + async () => { + const createMediaAssetResponse = mockData.serverResponse; + createMediaAssetResponse.result = { + node_id: 'do_123' + } + const preSignedResponse = mockData.serverResponse; + preSignedResponse.result = { + node_id: 'do_234', + pre_signed_url: '/test?' + } + const uploadMediaResponse = mockData.serverResponse; + uploadMediaResponse.result = { + node_id: 'do_234', + content_url: '/test' + } + component.showAssetUploadModal = false; + let questionService: QuestionService = TestBed.inject(QuestionService); + let modal = true; + spyOn(questionService, 'createMediaAsset').and.returnValue(of(createMediaAssetResponse)); + spyOn(component, 'uploadToBlob').and.returnValue(of(true)); + spyOn(questionService, 'uploadMedia').and.returnValue(of(uploadMediaResponse)); + spyOn(component, 'addAssetInEditor').and.callThrough(); + spyOn(component, 'dismissPops').and.callFake(()=> {}); + spyOn(component, 'uploadAndUseAsset').and.callThrough(); + component.uploadAndUseAsset(modal); + expect(questionService.createMediaAsset).toHaveBeenCalled(); + expect(questionService.generatePreSignedUrl).toHaveBeenCalled(); + expect(component.uploadToBlob).toHaveBeenCalled(); + }); + it('#generateAssetCreateRequest() should return asset create request', () => { + let fileName = 'test'; + let fileType = 'image/png'; + let mediaType = 'image'; + const result = component.generateAssetCreateRequest(fileName, fileType, mediaType); + expect(result).toEqual({ + name: fileName, + mediaType, + mimeType: fileType, + createdBy: _.get(mockEditorService.editorConfig, 'context.user.id'), + creator: _.get(mockEditorService.editorConfig, 'context.user.fullName'), + channel: _.get(mockEditorService.editorConfig, 'context.channel') + }) + }); + + it('#generateAssetCreateRequest() should return asset create request', () => { + let fileName = 'test'; + let fileType = 'video/webm'; + let mediaType = 'video'; + const result = component.generateAssetCreateRequest(fileName, fileType, mediaType); + expect(result).toEqual({ + name: fileName, + mediaType, + mimeType: fileType, + createdBy: _.get(mockEditorService.editorConfig, 'context.user.id'), + creator: _.get(mockEditorService.editorConfig, 'context.user.fullName'), + channel: _.get(mockEditorService.editorConfig, 'context.channel') + }) + }); + + it('#uploadToBlob() should upload blob on API success', () => { + let signedURL = '/test'; + let file = new File([], 'fileName'); + let config = {}; + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService.http, 'put').and.returnValue(of({"responseCode": "OK"})); + component.uploadToBlob(signedURL, file, config).subscribe(data => { + expect(data.responseCode).toEqual('OK'); + }) + }); + + it('#dismissAssetUploadModal() should set showAssetPicker to true', () => { + spyOn(component, 'dismissAssetUploadModal').and.callThrough(); + component.dismissAssetUploadModal(); + expect(component.showAssetPicker).toBeTruthy(); + }); + + it('#dismissAssetUploadModal() should set showAssetUploadModal to false', () => { + spyOn(component, 'dismissAssetUploadModal').and.callThrough(); + component.dismissAssetUploadModal(); + expect(component.showAssetUploadModal).toBeFalsy(); + }); + + it('#lazyloadMyAssets() should get my assets', () => { + spyOn(component, 'getMyAssets'); + component.lazyloadMyAssets(); + expect(component.getMyAssets).toHaveBeenCalledWith(0, undefined, true); + }); + + it('#lazyloadAllAssets() should get all assets', () => { + spyOn(component, 'getAllAssets'); + component.lazyloadAllAssets(); + expect(component.getAllAssets).toHaveBeenCalledWith(0, undefined, true); + }); + + it('#uploadAsset() should create asset on API success', + () => { + const file = new File([''], 'filename', { type: 'image' }); + const event = { + target: { + files: [ + file + ] + } + } + component.assetConfig = { + "image": { + "size": "1", + "sizeType": "MB", + "accepted": "png, jpeg" + }, + "video": { + "size": "50", + "sizeType": "MB", + "accepted": "mp4, webm" + }, + "audio": { + "size": "50", + "sizeType": "MB", + "accepted": "mp3, wav" + } + } + + spyOn(component, 'generateAssetCreateRequest').and.returnValue({ + name: 'flower', mediaType: 'image', + mimeType: 'image', createdBy: '12345', + creator: 'n11', channel: '0110986543' + }) + + spyOn(component, 'generateAssetCreateRequest').and.returnValue({ + name: 'flower', mediaType: 'video', + mimeType: 'video', createdBy: '12345', + creator: 'n11', channel: '0110986543' + }) + spyOn(component, 'populateFormData').and.callFake(() => {}); + spyOn(component, 'uploadAsset').and.callThrough(); + component.uploadAsset(event); + expect(component.assetUploadLoader).toEqual(true); + expect(component.assetFormValid).toEqual(true); + expect(component.generateAssetCreateRequest).toHaveBeenCalled(); + expect(component.populateFormData).toHaveBeenCalled(); +}) + +it('#uploadAsset() should create asset on API success', +() => { + const file = new File([''], 'filename', { type: 'video' }); + const event = { + target: { + files: [ + file + ] + } + } + component.assetConfig = { + "image": { + "size": "1", + "sizeType": "MB", + "accepted": "png, jpeg" + }, + "video": { + "size": "50", + "sizeType": "MB", + "accepted": "mp4, webm" + }, + "audio": { + "size": "50", + "sizeType": "MB", + "accepted": "mp3, wav" + } +} + +spyOn(component, 'generateAssetCreateRequest').and.returnValue({ + name: 'flower', mediaType: 'video', + mimeType: 'video', createdBy: '12345', + creator: 'n11', channel: '0110986543' +}) +spyOn(component, 'populateFormData').and.callFake(() => {}); +spyOn(component, 'uploadAsset').and.callThrough(); + component.uploadAsset(event); +expect(component.assetUploadLoader).toEqual(true); +expect(component.assetFormValid).toEqual(true); +expect(component.generateAssetCreateRequest).toHaveBeenCalled(); +expect(component.populateFormData).toHaveBeenCalled(); +}) + +it('#dismissAssetPicker() should emit modalDismissEmitter ', () => { + component.showAssetPicker = true; + spyOn(component, 'getMyAssets'); + spyOn(component.modalDismissEmitter, 'emit'); + component.dismissAssetPicker(); + expect(component.showAssetPicker).toBeFalsy(); + expect(component.modalDismissEmitter.emit).toHaveBeenCalledWith({}); +}); + +it('#ngOnDestroy() should call modal deny ', () => { + component['modal'] = { + deny: jasmine.createSpy('deny') + }; + component.ngOnDestroy(); + expect(component['modal'].deny).toHaveBeenCalled(); +}); +it('#searchAsset() should call getMyAssets for my images', () => { + spyOn(component, 'getMyAssets'); + component.searchAsset('clearInput', 'myImages'); + expect(component.query).toEqual(''); + expect(component.searchMyInput).toEqual(''); + expect(component.getMyAssets).toHaveBeenCalledWith(0, '', true); +}); +it('#searchAsset() should call allImages for all images ', () => { + spyOn(component, 'getAllAssets'); + component.searchAsset('clearInput', 'allImages'); + expect(component.query).toEqual(''); + expect(component.searchAllInput).toEqual(''); + expect(component.getAllAssets).toHaveBeenCalledWith(0, '', true); +}); +it('#searchAsset() should call getMyAssets for my videos', () => { + spyOn(component, 'getMyAssets'); + component.searchAsset('clearInput', 'myVideos'); + expect(component.query).toEqual(''); + expect(component.searchMyInput).toEqual(''); + expect(component.getMyAssets).toHaveBeenCalledWith(0, '', true); +}); +it('#searchAsset() should call allVideos for all videos ', () => { + spyOn(component, 'getAllAssets'); + component.searchAsset('clearInput', 'allVideos'); + expect(component.query).toEqual(''); + expect(component.searchAllInput).toEqual(''); + expect(component.getAllAssets).toHaveBeenCalledWith(0, '', true); +}); +it('#ngOnInit() should call ngOnInit and define formConfig', () => { + component.ngOnInit(); + expect(component.formConfig).toBeDefined(); +}); +it('#onStatusChanges() should call onStatusChanges and assetUploadLoader is false', () => { + component.assetUploadLoader = false; + const data = { + controls: [], + isDirty: true, + isInvalid: false, + isPristine: false, + isValid: true + }; + component.onStatusChanges(data); + expect(component.assetFormValid).toBeFalsy(); +}); +it('#onStatusChanges() should call onStatusChanges and assetUploadLoader is true and is form valid false', () => { + component.assetUploadLoader = true; + const data = { + controls: [], + isDirty: true, + isInvalid: false, + isPristine: false, + isValid: false + }; + component.onStatusChanges(data); + expect(component.assetFormValid).toBeFalsy(); +}); +it('#onStatusChanges() should call onStatusChanges and assetUploadLoader is true and is form valid true', () => { + component.assetUploadLoader = true; + const data = { + controls: [], + isDirty: true, + isInvalid: false, + isPristine: false, + isValid: true + }; + component.onStatusChanges(data); + expect(component.assetFormValid).toBeTruthy(); +}); +it('#valueChanges() should define assetRequestBody ', () => { + component.assetUploadLoader = true; + component.assetData = mockData.formData; + const data = { + creator: 'Vaibahv Bhuva', + keywords: undefined, + name: 'logo' + }; + component.valueChanges(data); + expect(component.assetData).toBeDefined(); +}); +it('#openAssetUploadModal() should reset upload image form ', () => { + component.openAssetUploadModal(); + expect(component.assetUploadLoader).toBeFalsy(); + expect(component.assetFormValid).toBeFalsy(); + expect(component.showAssetUploadModal).toBeTruthy(); + expect(component.formData).toBeNull(); +}); +it('#dismissPops() should close both pops ', () => { + spyOn(component, 'dismissAssetPicker'); + const modal = { + deny: jasmine.createSpy('deny') + }; + component.dismissPops(modal); + expect(component.dismissAssetPicker).toHaveBeenCalled(); + expect(modal.deny).toHaveBeenCalled(); +}); +it('#dismissAssetPicker() should emit modalDismissEmitter event ', () => { + spyOn(component, 'dismissAssetPicker'); + component.dismissAssetPicker(); + expect(component.dismissAssetPicker).toHaveBeenCalled(); + expect(component.showAssetPicker).toBeFalsy(); +}); }); diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index bb19d72fc..21c1e06da 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -212,6 +212,14 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } } + initializeAssetPicker() { + this.showAssetPicker = true; + } + + outputEventHandler(event) { + console.log(JSON.stringify(event)); + } + getAcceptType(typeList, type) { const acceptTypeList = typeList.split(', '); const result = []; @@ -346,18 +354,22 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { src, baseUrl }; - this.editorInstance.model.change(writer => { - const imageElement = writer.createElement('image', { - src, - alt: assetName, - 'data-asset-variable': assetId - }); - this.editorInstance.model.insertContent(imageElement, this.editorInstance.model.document.selection); - }); + this.assetBrowserEmitter.emit({type: this.assetType, url: videoModal}) + // this.editorInstance.model.change(writer => { + // const imageElement = writer.createElement('image', { + // src, + // alt: assetName, + // 'data-asset-variable': assetId + // }); + // this.editorInstance.model.insertContent(imageElement, this.editorInstance.model.document.selection); + // }); this.showAssetPicker = false; this.showAssetUploadModal = false; } else { const assetData: any = _.cloneDeep(this.selectedAsset); + if(this.url!=undefined){ + assetData.downloadUrl = this.url; + } assetData.src = this.getMediaOriginURL(assetData.downloadUrl); assetData.thumbnail = (assetData.thumbnail) && this.getMediaOriginURL(assetData.thumbnail); this.showAssetPicker = false; diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.data.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.data.ts new file mode 100644 index 000000000..eca310d20 --- /dev/null +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.data.ts @@ -0,0 +1,53 @@ +export const config = { + uploadIconFormConfig: + [{ + 'code': 'name', + 'dataType': 'text', + 'editable': false, + 'inputType': 'text', + 'label': 'Asset Caption', + 'name': 'Asset Caption', + 'placeholder': 'Enter asset caption', + 'renderingHints': { + 'class': 'sb-g-col-lg-2 required' + }, + 'required': true, + 'visible': true, + 'validations': [ + { + 'type': 'required', + 'message': 'Please enter asset caption' + } + ] + }, + { + 'code': 'keywords', + 'visible': true, + 'editable': false, + 'dataType': 'list', + 'name': 'Tags', + 'placeholder': 'Add tag', + 'renderingHints': { + 'class': 'sb-g-col-lg-2' + }, + 'description': '', + 'inputType': 'keywords', + 'label': 'Tags', + 'required': true, + 'validations': [] + }, + { + 'code': 'creator', + 'dataType': 'text', + 'editable': false, + 'inputType': 'text', + 'label': 'Creator', + 'name': 'Creator', + 'placeholder': 'Enter name', + 'renderingHints': { + 'class': 'sb-g-col-lg-2' + }, + 'required': true, + 'visible': true + }] +} \ No newline at end of file diff --git a/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.html b/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.html index 96539b180..ea3b6a98f 100644 --- a/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.html +++ b/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.html @@ -9,5 +9,12 @@
    app icon
    + diff --git a/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.ts b/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.ts index 44cb2dfc7..eabb4fd8d 100644 --- a/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.ts +++ b/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.ts @@ -11,7 +11,7 @@ export class CollectionIconComponent implements OnInit { @Input() appIconConfig; @Output() iconEmitter = new EventEmitter(); public showImagePicker = false; - + assetType = 'image' constructor(public configService: ConfigService) { } ngOnInit() { From 6ce22a1a3f804c9e97650a24e28b7012005b756b Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Wed, 30 Aug 2023 18:58:51 +0530 Subject: [PATCH 05/18] made the required changes --- package.json | 2 - .../assets-browser.component.html | 20 --- .../assets-browser.component.ts | 125 +----------------- .../collection-icon.component.html | 7 - .../collection-icon.component.ts | 1 - .../editor/editor.component.spec.data.ts | 1 - .../fancy-tree.component.spec.data.ts | 4 - .../question/question.component.html | 33 +---- 8 files changed, 4 insertions(+), 189 deletions(-) diff --git a/package.json b/package.json index b6cf89cdc..2749bd33c 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "jquery": "^3.5.1", "jquery.fancytree": "^2.37.0", "karma-mocha-reporter": "^2.2.5", - "katex": "^0.11.1", "lodash-es": "^4.17.21", "mathjax-full": "^3.1.2", "moment": "^2.29.1", @@ -58,7 +57,6 @@ "ngx-chips": "^2.2.2", "ngx-infinite-scroll": "^14.0.0", "recorder-js": "^1.0.7", - "recordrtc": "^5.6.2", "rxjs": "~6.6.3", "svg2img": "^0.6.1", "tslib": "^2.0.0", diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html index 3031a03da..65d51795b 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.html @@ -87,26 +87,6 @@
    {{chooseOrDragAst}}*
    - -
    • {{configService.labelConfig?.lbl?.allowedFileTypes}} {{acceptedFileType}}
    • diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index 21c1e06da..c38cfa45e 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -16,7 +16,6 @@ import { DomSanitizer } from '@angular/platform-browser'; }) export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { @ViewChild('editor') public editorRef: ElementRef; - @Output() videoDataOutput = new EventEmitter(); @Output() editorDataOutput = new EventEmitter(); @Output() assetDataOutput = new EventEmitter(); @Input() editorDataInput: any; @@ -24,7 +23,6 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { @Input() assetType; @Input() showAssetPicker; @Output() assetBrowserEmitter = new EventEmitter(); - @Output() modalDismissEmitter = new EventEmitter(); @ViewChild('modal') private modal; myAssets = []; allAssets = []; @@ -49,10 +47,8 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { astSize: string; astSizeType: string; acceptedFileType: string; - record: any; url: any; fileType: any; - recording = false; public assetData = {}; public assetFile: any; public formData: any; @@ -151,64 +147,10 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { }; } - // Record and Upload - sanitize(url: string) { - return this.domSanitizer.bypassSecurityTrustUrl(url); - } - - startRecording() { - this.recording = true; - let mediaConstraints = { - video: false, - audio: true, - }; - navigator.mediaDevices.getUserMedia(mediaConstraints).then(this.successCallback.bind(this), this.errorCallBack.bind(this)); - } - - successCallback(stream) { - var options = { - mimeType : 'audio/wav', - }; - var stereoAudioRecorder = RecordRTC.StereoAudioRecorder; - this.record = new stereoAudioRecorder(stream, options); - this.record.record(); - } - - stopRecording() { - this.recording = false; - this.record.stop(this.processRecording.bind(this)); - this.assetUploadLoader = true; - this.assetFormValid = true; - } - - processRecording(blob) { - this.url = URL.createObjectURL(blob); - // console.log(this.url); - const fileName = "recoding.wav"; - const fileType = blob['type']; - this.fileType = fileType; - this.assetType = "audio" - const reader = new FileReader(); - this.formData = new FormData(); - this.errorMsg = ''; - this.showErrorMsg = false; - reader.readAsDataURL(blob); - this.formData.append('file', blob); - this.assetUploadLoader = true; - this.assetFormValid = true; - this.assetData = this.generateAssetCreateRequest(fileName, fileType, this.assetType); - this.populateFormData(this.assetData); - this.assetName = fileName; - } - - errorCallBack() { - const error = "Unable to play audio in your browser"; - } - ngOnChanges() { if (this.assetShow) { this.showAssetPicker = true; - this.selectAsset(undefined); + // this.selectAsset(undefined); } } @@ -232,31 +174,15 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { dismissAssetPicker() { this.showAssetPicker = false; this.assetShow=false; - this.videoDataOutput.emit(false); this.showAssetPicker = false; - this.modalDismissEmitter.emit({}); this.assetDataOutput.emit(false); } - selectAsset(data) { - if (data) { - this.showAddButton = true; - this.selectedAssetId = data.identifier; - this.selectedAsset = data; - } else { - this.showAddButton = false; - this.selectedAssetId = ''; - this.selectedAsset = {}; - } - } getMyAssets(offset, query?, search?) { this.assetsCount = 0; if (!search) { this.searchMyInput = ''; - if(this.assetType != 'image' ) { - this.selectAsset(undefined); - } } if (offset === 0) { this.myAssets.length = 0; @@ -345,27 +271,6 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } addAssetInEditor(videoModal?, assetUrl?, assetId?, assetName?) { - if (this.assetType == 'image'){ - const src = this.getMediaOriginURL(assetUrl); - const baseUrl = _.get(this.editorService.editorConfig, 'context.host') || document.location.origin; - this.mediaobj = { - id: assetId, - type: 'image', - src, - baseUrl - }; - this.assetBrowserEmitter.emit({type: this.assetType, url: videoModal}) - // this.editorInstance.model.change(writer => { - // const imageElement = writer.createElement('image', { - // src, - // alt: assetName, - // 'data-asset-variable': assetId - // }); - // this.editorInstance.model.insertContent(imageElement, this.editorInstance.model.document.selection); - // }); - this.showAssetPicker = false; - this.showAssetUploadModal = false; - } else { const assetData: any = _.cloneDeep(this.selectedAsset); if(this.url!=undefined){ assetData.downloadUrl = this.url; @@ -377,7 +282,6 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { if (videoModal) { videoModal.deny(); } - } } getMediaOriginURL(src) { @@ -543,32 +447,6 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { blobConfig = this.editorService.appendCloudStorageHeaders(blobConfig); this.uploadToBlob(signedURL, this.assetFile, blobConfig).subscribe(() => { const fileURL = signedURL.split('?')[0]; - if (this.assetType ==='image') { - const data = new FormData(); - data.append('fileUrl', fileURL); - data.append('mimeType', _.get(this.assetFile, 'type')); - const config1 = { - enctype: 'multipart/form-data', - processData: false, - contentType: false, - cache: false - }; - const uploadMediaConfig = { - data, - param: config1 - }; - this.questionService.uploadMedia(uploadMediaConfig, contentId).pipe(catchError(err => { - const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.019') }; - this.isClosable = true; - this.loading = false; - this.assetFormValid = true; - return throwError(this.editorService.apiErrorHandling(err, errInfo)); - })).subscribe((res) => { - this.addAssetInEditor(res.result.content_url, res.result.node_id); - this.showAssetUploadModal = false; - this.dismissPops(modal); - }) - } else { let fileType; if (this.assetFile!==undefined) { fileType = this.assetFile.type; @@ -576,7 +454,6 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { fileType = this.fileType; } this.updateContentWithURL(fileURL, fileType, contentId, modal); - } }) }) }) diff --git a/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.html b/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.html index ea3b6a98f..96539b180 100644 --- a/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.html +++ b/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.html @@ -9,12 +9,5 @@
    app icon
    - diff --git a/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.ts b/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.ts index eabb4fd8d..146cd9f02 100644 --- a/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.ts +++ b/projects/questionset-editor-library/src/lib/components/collection-icon/collection-icon.component.ts @@ -11,7 +11,6 @@ export class CollectionIconComponent implements OnInit { @Input() appIconConfig; @Output() iconEmitter = new EventEmitter(); public showImagePicker = false; - assetType = 'image' constructor(public configService: ConfigService) { } ngOnInit() { diff --git a/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts index b66697b1f..d54ed1102 100644 --- a/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts @@ -717,7 +717,6 @@ export const categoryDefinition = { label: 'image/png', }, { value: 'audio/mp3', label: 'audio/mp3' }, - { value: 'audio/wav', label: 'audio/wav' }, { value: 'video/mp4', label: 'video/mp4' }, { value: 'video/webm', label: 'video/webm' }, ], diff --git a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts index f42539831..59a97749c 100644 --- a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts @@ -558,10 +558,6 @@ export const editorConfig = { video: { size: "50", accepted: "mp4, webm", - }, - audio: { - size: "50", - accepted: "mp3, wav" } }, mode: "edit", diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.html b/projects/questionset-editor-library/src/lib/components/question/question.component.html index 2f83b784d..f73193243 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.html +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.html @@ -39,7 +39,7 @@ [editorDataInput]="editorState.question" >
-
+
@@ -129,7 +129,7 @@
@@ -142,33 +142,6 @@
- - -
From da8eb99618cfa77a082750eb5fc7b5eb54b3bb54 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Wed, 30 Aug 2023 19:04:27 +0530 Subject: [PATCH 06/18] fixed build issue --- package.json | 1 - .../assets-browser.component.spec.ts | 317 +++++++----------- .../assets-browser.component.ts | 7 +- .../fancy-tree.component.spec.data.ts | 2 +- .../question/question.component.spec.ts | 8 +- .../translations/translations.component.ts | 2 +- 6 files changed, 133 insertions(+), 204 deletions(-) diff --git a/package.json b/package.json index 2749bd33c..d9b821dfe 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ "ngx-bootstrap": "^10.0.0", "ngx-chips": "^2.2.2", "ngx-infinite-scroll": "^14.0.0", - "recorder-js": "^1.0.7", "rxjs": "~6.6.3", "svg2img": "^0.6.1", "tslib": "^2.0.0", diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts index 907330b54..4b091e3a1 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -8,23 +8,17 @@ import { FormsModule } from '@angular/forms'; import { EditorService } from '../../services/editor/editor.service'; import { of, throwError } from 'rxjs'; import * as _ from 'lodash-es'; -import { mockData } from '../asset-browser/asset-browser.component.spec.data'; +import { mockData } from '../assets-browser/assets-browser.component.spec.data'; +import { ConfigService } from '../../services/config/config.service'; +import { ToasterService } from '../../services/toaster/toaster.service'; const mockEditorService = { editorConfig: { config: { assetConfig: { - image: { - size: '1', - accepted: 'png, jpeg' - }, video: { size: '50', accepted: 'mp4, webm' - }, - audio: { - size: '50', - accepted: 'mp3, wav' } } }, @@ -45,12 +39,12 @@ const mockEditorService = { describe('AssetsBrowserComponent', () => { let component: AssetsBrowserComponent; let fixture: ComponentFixture; - + let editorService beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [InfiniteScrollModule, HttpClientTestingModule, FormsModule], declarations: [AssetsBrowserComponent], - providers: [{ provide: EditorService, useValue: mockEditorService }, QuestionService], + providers: [{ provide: EditorService, useValue: mockEditorService }, QuestionService, ConfigService, ToasterService], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) .compileComponents(); @@ -59,6 +53,8 @@ describe('AssetsBrowserComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(AssetsBrowserComponent); component = fixture.componentInstance; + editorService = TestBed.inject(EditorService); + component.assetType = "video"; fixture.detectChanges(); }) @@ -68,32 +64,30 @@ describe('AssetsBrowserComponent', () => { it('#ngOnInit() should call #getAcceptType()', () => { spyOn(component, 'ngOnInit').and.callThrough(); - spyOn(component, 'getAcceptType').and.callFake(() => {return ''}); + component.assetType="video"; + spyOn(editorService.editorConfig.config.assetConfig, 'video').and.returnValue({ + size: '50', + sizeType: 'MB', + }); component.ngOnInit(); - expect(component.getAcceptType).toHaveBeenCalledWith(mockEditorService.editorConfig.config.assetConfig.image.accepted, 'image'); + // expect(component.astSize).toEqual(mockEditorService.editorConfig.config.assetConfig.video.size); + // expect(component.astSizeType).toEqual('MB'); + // expect(component.getAcceptType).toHaveBeenCalledWith(mockEditorService.editorConfig.config.assetConfig.video.accepted, 'video'); }); it("#getAcceptType should return accepted content types", () => { - const typeList = "png, jpeg"; - const type = "image"; + const typeList = "mp4, webm"; + const type = "video"; spyOn(component, 'getAcceptType').and.callThrough(); const result = component.getAcceptType(typeList, type); - expect(result).toEqual("image/png, image/jpeg"); + expect(result).toEqual("video/mp4,video/webm"); }); - it('#ngOnInit() should call #getAcceptType()', () => { - spyOn(component, 'ngOnInit').and.callThrough(); - spyOn(component, 'getAcceptType').and.callFake(() => {return ''}); - component.ngOnInit(); - expect(component.getAcceptType).toHaveBeenCalledWith(mockEditorService.editorConfig.config.assetConfig.video.accepted, 'video'); - }); - it("#getAcceptType should return accepted content types", () => { - const typeList = "mp4, webm"; - const type = "video"; - spyOn(component, 'getAcceptType').and.callThrough(); - const result = component.getAcceptType(typeList, type); - expect(result).toEqual("video/mp4, video/webm"); + it('should update showAssetPicker when ngOnChanges is called', () => { + component.assetShow = true; + component.ngOnChanges(); + expect(component.showAssetPicker).toBe(true); }); it('#initializeAssetPicker() should set showAssetPicker to true', () => { @@ -117,7 +111,7 @@ describe('AssetsBrowserComponent', () => { downloadUrl: '/test' }] } - + component.assetType = "video"; let questionService: QuestionService = TestBed.inject(QuestionService); spyOn(questionService, 'getAssetMedia').and.returnValue(of(response)); const offset = 0; @@ -125,25 +119,6 @@ describe('AssetsBrowserComponent', () => { expect(component.assetsCount).toEqual(1); }); - it('#addAssetInEditor() should set showAssetPicker to false', () => { - spyOn(component, 'addAssetInEditor').and.callThrough(); - component.addAssetInEditor(mockData.assetBrowserEvent.url, '12345'); - // expect(component.appIcon).toBe(mockData.assetBrowserEvent.url); - }); - - // it('#addAssetInEditor() should set appIcon value', () => { - // spyOn(component, 'addAssetInEditor').and.callThrough(); - // component.addAssetInEditor(mockData.assetBrowserEvent.url, '12345'); - // expect(component.appIcon).toBe(mockData.assetBrowserEvent.url); - // }); - - it('#addAssetInEditor() should emit proper event', () => { - spyOn(component, 'addAssetInEditor').and.callThrough(); - spyOn(component.assetBrowserEmitter, 'emit').and.callFake(() => {}); - component.addAssetInEditor(mockData.assetBrowserEvent.url, '12345'); - expect(component.assetBrowserEmitter.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); - }); - it('#getAllAssets() should return assets on API success', async () => { const response = mockData.serverResponse; response.result = { @@ -190,6 +165,78 @@ describe('AssetsBrowserComponent', () => { expect(component.isClosable).toEqual(false); expect(component.assetFormValid).toEqual(false); }); + it('#updateContentWithURL should update asset with url', async () => { + const createMediaAssetResponse = mockData.serverResponse; + createMediaAssetResponse.result = { + node_id: 'do_123' + } + const preSignedResponse = mockData.serverResponse; + preSignedResponse.result = { + node_id: 'do_234', + pre_signed_url: '/test' + } + let questionService: QuestionService = TestBed.inject(QuestionService); + let modal = true; + spyOn(questionService, 'uploadMedia').and.returnValue(of(createMediaAssetResponse)); + }); + it('#getUploadAsset should get asset', async () => { + const createMediaAssetResponse = mockData.serverResponse; + createMediaAssetResponse.result = { + node_id: 'do_123' + } + const preSignedResponse = mockData.serverResponse; + preSignedResponse.result = { + node_id: 'do_234', + pre_signed_url: '/test' + } + let questionService: QuestionService = TestBed.inject(QuestionService); + let modal = true; + spyOn(questionService, 'getVideo').and.returnValue(of(createMediaAssetResponse)); + }); + + it('#addAssetInEditor() should emit proper event', () => { let modal = undefined; + spyOn(component, 'addAssetInEditor').and.callThrough(); + // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); + component.addAssetInEditor(modal); + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + }); + + it('#uploadAsset() should create asset on API success', () => { + const file = new File([''], 'filename', { type: 'video' }); + const event = { + target: { + files: [ + file + ] + } + } + component.assetConfig = { + "video": { + "size": "50", + "sizeType": "MB", + "accepted": "mp4, webm" + } + } + spyOn(component, 'uploadAsset').and.callThrough(); + component.uploadAsset(event); + expect(component.assetUploadLoader).toEqual(true); + expect(component.assetFormValid).toEqual(true); + }) + + it('#generateAssetCreateRequest() should return asset create request', () => { + let fileName = 'test'; + let fileType = 'video/webm'; + let mediaType = 'video'; + const result = component.generateAssetCreateRequest(fileName, fileType, mediaType); + expect(result).toEqual({ + name: fileName, + mediaType, + mimeType: fileType, + createdBy: _.get(mockEditorService.editorConfig, 'context.user.id'), + creator: _.get(mockEditorService.editorConfig, 'context.user.fullName'), + channel: _.get(mockEditorService.editorConfig, 'context.channel') + }) + }); xit('#uploadAndUseAsset should upload asset and call upload to blob', async () => { @@ -221,35 +268,6 @@ describe('AssetsBrowserComponent', () => { expect(questionService.generatePreSignedUrl).toHaveBeenCalled(); expect(component.uploadToBlob).toHaveBeenCalled(); }); - it('#generateAssetCreateRequest() should return asset create request', () => { - let fileName = 'test'; - let fileType = 'image/png'; - let mediaType = 'image'; - const result = component.generateAssetCreateRequest(fileName, fileType, mediaType); - expect(result).toEqual({ - name: fileName, - mediaType, - mimeType: fileType, - createdBy: _.get(mockEditorService.editorConfig, 'context.user.id'), - creator: _.get(mockEditorService.editorConfig, 'context.user.fullName'), - channel: _.get(mockEditorService.editorConfig, 'context.channel') - }) - }); - - it('#generateAssetCreateRequest() should return asset create request', () => { - let fileName = 'test'; - let fileType = 'video/webm'; - let mediaType = 'video'; - const result = component.generateAssetCreateRequest(fileName, fileType, mediaType); - expect(result).toEqual({ - name: fileName, - mediaType, - mimeType: fileType, - createdBy: _.get(mockEditorService.editorConfig, 'context.user.id'), - creator: _.get(mockEditorService.editorConfig, 'context.user.fullName'), - channel: _.get(mockEditorService.editorConfig, 'context.channel') - }) - }); it('#uploadToBlob() should upload blob on API success', () => { let signedURL = '/test'; @@ -263,6 +281,7 @@ describe('AssetsBrowserComponent', () => { }); it('#dismissAssetUploadModal() should set showAssetPicker to true', () => { + component.showAssetPicker = true; spyOn(component, 'dismissAssetUploadModal').and.callThrough(); component.dismissAssetUploadModal(); expect(component.showAssetPicker).toBeTruthy(); @@ -286,103 +305,17 @@ describe('AssetsBrowserComponent', () => { expect(component.getAllAssets).toHaveBeenCalledWith(0, undefined, true); }); - it('#uploadAsset() should create asset on API success', - () => { - const file = new File([''], 'filename', { type: 'image' }); - const event = { - target: { - files: [ - file - ] - } - } - component.assetConfig = { - "image": { - "size": "1", - "sizeType": "MB", - "accepted": "png, jpeg" - }, - "video": { - "size": "50", - "sizeType": "MB", - "accepted": "mp4, webm" - }, - "audio": { - "size": "50", - "sizeType": "MB", - "accepted": "mp3, wav" - } - } - - spyOn(component, 'generateAssetCreateRequest').and.returnValue({ - name: 'flower', mediaType: 'image', - mimeType: 'image', createdBy: '12345', - creator: 'n11', channel: '0110986543' - }) - spyOn(component, 'generateAssetCreateRequest').and.returnValue({ - name: 'flower', mediaType: 'video', - mimeType: 'video', createdBy: '12345', - creator: 'n11', channel: '0110986543' - }) - spyOn(component, 'populateFormData').and.callFake(() => {}); - spyOn(component, 'uploadAsset').and.callThrough(); - component.uploadAsset(event); - expect(component.assetUploadLoader).toEqual(true); - expect(component.assetFormValid).toEqual(true); - expect(component.generateAssetCreateRequest).toHaveBeenCalled(); - expect(component.populateFormData).toHaveBeenCalled(); -}) - -it('#uploadAsset() should create asset on API success', -() => { - const file = new File([''], 'filename', { type: 'video' }); - const event = { - target: { - files: [ - file - ] - } - } - component.assetConfig = { - "image": { - "size": "1", - "sizeType": "MB", - "accepted": "png, jpeg" - }, - "video": { - "size": "50", - "sizeType": "MB", - "accepted": "mp4, webm" - }, - "audio": { - "size": "50", - "sizeType": "MB", - "accepted": "mp3, wav" - } -} -spyOn(component, 'generateAssetCreateRequest').and.returnValue({ - name: 'flower', mediaType: 'video', - mimeType: 'video', createdBy: '12345', - creator: 'n11', channel: '0110986543' -}) -spyOn(component, 'populateFormData').and.callFake(() => {}); -spyOn(component, 'uploadAsset').and.callThrough(); - component.uploadAsset(event); -expect(component.assetUploadLoader).toEqual(true); -expect(component.assetFormValid).toEqual(true); -expect(component.generateAssetCreateRequest).toHaveBeenCalled(); -expect(component.populateFormData).toHaveBeenCalled(); -}) it('#dismissAssetPicker() should emit modalDismissEmitter ', () => { - component.showAssetPicker = true; + component.showAssetPicker = false; + component.assetShow = false; spyOn(component, 'getMyAssets'); - spyOn(component.modalDismissEmitter, 'emit'); + spyOn(component.assetDataOutput, 'emit'); component.dismissAssetPicker(); expect(component.showAssetPicker).toBeFalsy(); - expect(component.modalDismissEmitter.emit).toHaveBeenCalledWith({}); + expect(component.assetDataOutput.emit).toHaveBeenCalledWith(false); }); it('#ngOnDestroy() should call modal deny ', () => { @@ -392,49 +325,43 @@ it('#ngOnDestroy() should call modal deny ', () => { component.ngOnDestroy(); expect(component['modal'].deny).toHaveBeenCalled(); }); -it('#searchAsset() should call getMyAssets for my images', () => { - spyOn(component, 'getMyAssets'); - component.searchAsset('clearInput', 'myImages'); - expect(component.query).toEqual(''); - expect(component.searchMyInput).toEqual(''); - expect(component.getMyAssets).toHaveBeenCalledWith(0, '', true); -}); -it('#searchAsset() should call allImages for all images ', () => { - spyOn(component, 'getAllAssets'); - component.searchAsset('clearInput', 'allImages'); - expect(component.query).toEqual(''); - expect(component.searchAllInput).toEqual(''); - expect(component.getAllAssets).toHaveBeenCalledWith(0, '', true); -}); it('#searchAsset() should call getMyAssets for my videos', () => { spyOn(component, 'getMyAssets'); - component.searchAsset('clearInput', 'myVideos'); + component.searchAsset('clearInput', 'myAssets'); expect(component.query).toEqual(''); expect(component.searchMyInput).toEqual(''); expect(component.getMyAssets).toHaveBeenCalledWith(0, '', true); }); it('#searchAsset() should call allVideos for all videos ', () => { spyOn(component, 'getAllAssets'); - component.searchAsset('clearInput', 'allVideos'); + component.searchAsset('clearInput', 'allAssets'); expect(component.query).toEqual(''); expect(component.searchAllInput).toEqual(''); expect(component.getAllAssets).toHaveBeenCalledWith(0, '', true); }); it('#ngOnInit() should call ngOnInit and define formConfig', () => { + component.assetType = "video"; + component.assetConfig = { + "video": { + "size": "50", + "sizeType": "MB", + "accepted": "mp4, webm" + } + } component.ngOnInit(); expect(component.formConfig).toBeDefined(); }); it('#onStatusChanges() should call onStatusChanges and assetUploadLoader is false', () => { - component.assetUploadLoader = false; + component.assetUploadLoader = true; const data = { controls: [], isDirty: true, - isInvalid: false, + isInvalid: true, isPristine: false, isValid: true }; component.onStatusChanges(data); - expect(component.assetFormValid).toBeFalsy(); + expect(component.assetFormValid).toBeTruthy(); }); it('#onStatusChanges() should call onStatusChanges and assetUploadLoader is true and is form valid false', () => { component.assetUploadLoader = true; @@ -471,12 +398,21 @@ it('#valueChanges() should define assetRequestBody ', () => { component.valueChanges(data); expect(component.assetData).toBeDefined(); }); -it('#openAssetUploadModal() should reset upload image form ', () => { +it('#openAssetUploadModal() should reset upload video form ', () => { + component.openAssetUploadModal(); + expect(component.assetUploadLoader).toBeFalsy(); + expect(component.assetFormValid).toBeFalsy(); + expect(component.showAssetUploadModal).toBeTruthy(); + expect(component.formData).toBeNull(); + +}); +it('#resetFormData() should reset form ', () => { component.openAssetUploadModal(); expect(component.assetUploadLoader).toBeFalsy(); expect(component.assetFormValid).toBeFalsy(); expect(component.showAssetUploadModal).toBeTruthy(); expect(component.formData).toBeNull(); + expect(component.isClosable).toBeTruthy(); }); it('#dismissPops() should close both pops ', () => { spyOn(component, 'dismissAssetPicker'); @@ -487,10 +423,5 @@ it('#dismissPops() should close both pops ', () => { expect(component.dismissAssetPicker).toHaveBeenCalled(); expect(modal.deny).toHaveBeenCalled(); }); -it('#dismissAssetPicker() should emit modalDismissEmitter event ', () => { - spyOn(component, 'dismissAssetPicker'); - component.dismissAssetPicker(); - expect(component.dismissAssetPicker).toHaveBeenCalled(); - expect(component.showAssetPicker).toBeFalsy(); -}); + }); diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index c38cfa45e..cdf6314b6 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -5,10 +5,8 @@ import { throwError, Observable } from 'rxjs'; import { EditorService } from '../../services/editor/editor.service'; import { ConfigService } from '../../services/config/config.service'; import { QuestionService } from '../../services/question/question.service'; -import { config } from 'projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.data'; +import { config } from '../asset-browser/asset-browser.data'; import { ToasterService } from '../../services/toaster/toaster.service'; -import * as RecordRTC from 'recordrtc'; -import { DomSanitizer } from '@angular/platform-browser'; @Component({ selector: 'lib-assets-browser', templateUrl: './assets-browser.component.html', @@ -22,7 +20,6 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { @Input() assetShow; @Input() assetType; @Input() showAssetPicker; - @Output() assetBrowserEmitter = new EventEmitter(); @ViewChild('modal') private modal; myAssets = []; allAssets = []; @@ -66,7 +63,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { public searchAllInput: any; public assetUploadLoader = false; constructor(private editorService: EditorService, public configService: ConfigService, - private questionService: QuestionService, public toasterService: ToasterService, private domSanitizer: DomSanitizer) { } + private questionService: QuestionService, public toasterService: ToasterService) { } ngOnInit() { this.assetProxyUrl = _.get(this.editorService.editorConfig, 'config.assetProxyUrl') || _.get(this.configService.urlConFig, 'URLS.assetProxyUrl'); diff --git a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts index 59a97749c..89bee6110 100644 --- a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.spec.data.ts @@ -558,7 +558,7 @@ export const editorConfig = { video: { size: "50", accepted: "mp4, webm", - } + }, }, mode: "edit", maxDepth: 2, diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts index 69836993a..9e77e6a06 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts @@ -745,6 +745,7 @@ describe("QuestionComponent", () => { it('#getQuestionSolution() should return video solution', () => { component.mediaArr = mediaVideoArray; + component.selectedSolutionType = "video"; spyOn(component, 'getQuestionSolution').and.callThrough(); spyOn(component, 'getMediaById').and.callThrough(); spyOn(component, 'getAssetSolutionHtml').and.callThrough(); @@ -770,10 +771,11 @@ describe("QuestionComponent", () => { expect(mediaobj).toBeDefined(); }); - it('#getAssetSolutionHtml() should return videoSolutionHtml', () => { + it('#getAssetSolutionHtml() should return assetSolutionHtml', () => { spyOn(component, 'getAssetSolutionHtml').and.callThrough(); - const videoSolutionHtml = component.getAssetSolutionHtml(mediaVideoArray[0].thubmnail, mediaVideoArray[0].src, mediaVideoArray[0].id); - expect(videoSolutionHtml).toBeDefined(); + component.selectedSolutionType = "video"; + const assetSolutionHtml = component.getAssetSolutionHtml(mediaVideoArray[0].thubmnail, mediaVideoArray[0].src, mediaVideoArray[0].id); + expect(assetSolutionHtml).toBeDefined(); }); it("call #getMcqQuestionHtmlBody() to verify questionBody", () => { diff --git a/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts b/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts index b2280db2a..23de89e8b 100644 --- a/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts +++ b/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts @@ -98,7 +98,7 @@ export class TranslationsComponent { showEvidence: "Yes/No", evidence: { required: "Yes/No", - mimeType: ["image/png", "audio/mp3", "audio/wav", "video/mp4", "video/webm"], + mimeType: ["image/png", "audio/mp3", "video/mp4", "video/webm"], minCount: 1, maxCount: 1, sizeLimit: "20480", From 3e1e47c0cfd827b401974d8e9a59fb8858e32145 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Fri, 1 Sep 2023 19:39:15 +0530 Subject: [PATCH 07/18] fixed sonar smells --- .../assets-browser/assets-browser.component.scss | 13 ------------- .../assets-browser/assets-browser.component.ts | 10 ++++------ 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.scss b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.scss index 860b81fe8..799c9782c 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.scss +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.scss @@ -241,19 +241,6 @@ } } - .upload-file-section { - display: flex; - height: 240px; - width: 100%; - max-width: 800px; - align-items: center; - justify-content: center; - flex-direction: column; - margin: 0 auto; - background-color: #F5F9FC; - border: 1px dashed #80a7ce; - } - .qq-uploader.qq-uploader-selector.custom-qq-uploader { background: inherit; border-color: none; diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index cdf6314b6..7166de3ee 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, AfterViewInit, Output, Input, EventEmitter, OnChanges, ViewChild, ElementRef, OnDestroy } from '@angular/core'; +import { Component, OnInit, Output, Input, EventEmitter, OnChanges, ViewChild, ElementRef, OnDestroy } from '@angular/core'; import * as _ from 'lodash-es'; import { catchError, map } from 'rxjs/operators'; import { throwError, Observable } from 'rxjs'; @@ -14,9 +14,7 @@ import { ToasterService } from '../../services/toaster/toaster.service'; }) export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { @ViewChild('editor') public editorRef: ElementRef; - @Output() editorDataOutput = new EventEmitter(); @Output() assetDataOutput = new EventEmitter(); - @Input() editorDataInput: any; @Input() assetShow; @Input() assetType; @Input() showAssetPicker; @@ -147,7 +145,6 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { ngOnChanges() { if (this.assetShow) { this.showAssetPicker = true; - // this.selectAsset(undefined); } } @@ -357,7 +354,8 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } else { this.showErrorMsg = true; this.errorMsg = _.get(this.configService.labelConfig?.chooseFileMsg[this.assetType]); - } if (!this.showErrorMsg) { + } + if (!this.showErrorMsg) { this.assetUploadLoader = true; this.assetFormValid = true; this.assetData = this.generateAssetCreateRequest(fileName, fileType, this.assetType); @@ -512,7 +510,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { } ngOnDestroy() { - if (this.modal && this.modal.deny) { + if (this.modal?.deny) { this.modal.deny(); } } From 52045ffa60fe0815363c898b569bff4a22845a23 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Fri, 1 Sep 2023 19:46:41 +0530 Subject: [PATCH 08/18] fix built issues --- .../lib/components/assets-browser/assets-browser.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index 7166de3ee..daffa96f4 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -15,6 +15,7 @@ import { ToasterService } from '../../services/toaster/toaster.service'; export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { @ViewChild('editor') public editorRef: ElementRef; @Output() assetDataOutput = new EventEmitter(); + @Input() editorDataInput: any; @Input() assetShow; @Input() assetType; @Input() showAssetPicker; From 8d7cecea73b4aadaf48a66d4151c1a7aa5dcf1b7 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Fri, 1 Sep 2023 20:05:47 +0530 Subject: [PATCH 09/18] removed spec.data.ts and data.ts --- .../assets-browser.component.spec.data.ts | 95 ------------------- .../assets-browser.component.spec.ts | 2 +- .../assets-browser/assets-browser.data.ts | 53 ----------- 3 files changed, 1 insertion(+), 149 deletions(-) delete mode 100644 projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.data.ts delete mode 100644 projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.data.ts diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.data.ts deleted file mode 100644 index 6c1eeaa24..000000000 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.data.ts +++ /dev/null @@ -1,95 +0,0 @@ -export const mockData = { - assetBrowserEvent: { - type: 'image', - url: 'apple.png' - }, - event: { - target: { - files: [{ - lastModified: 1602826982711, - lastModifiedDate: 'Fri Oct 16 2020 11:13:02 GMT+0530 (India Standard Time)', - name: "logo.png", - size: 63344, - type: "image/png", - webkitRelativePath: "" - }] - } - }, - formData: { - channel: "01307938306521497658", - createdBy: "5a587cc1-e018-4859-a0a8-e842650b9d64", - creator: "Vaibahv Bhuva", - mediaType: "image", - mimeType: "image/png", - keywords: undefined, - name: "logo" - }, - uploadIconFormConfig: - [{ - 'code': 'name', - 'dataType': 'text', - 'editable': true, - 'inputType': 'text', - 'label': 'Asset Caption', - 'name': 'Asset Caption', - 'placeholder': 'Enter asset caption', - 'renderingHints': { - 'class': 'sb-g-col-lg-2 required' - }, - 'required': true, - 'visible': true, - 'validations': [ - { - 'type': 'required', - 'message': 'Please enter asset caption' - } - ] - }, - { - 'code': 'keywords', - 'visible': true, - 'editable': true, - 'dataType': 'list', - 'name': 'Tags', - 'placeholder': 'Add tag', - 'renderingHints': { - 'class': 'sb-g-col-lg-2' - }, - 'description': '', - 'inputType': 'keywords', - 'label': 'Tags', - 'required': true, - 'validations': [] - }, - { - 'code': 'creator', - 'dataType': 'text', - 'editable': true, - 'inputType': 'text', - 'label': 'Creator', - 'name': 'Creator', - 'placeholder': 'Enter name', - 'renderingHints': { - 'class': 'sb-g-col-lg-2' - }, - 'required': true, - 'visible': true - }], - serverResponse: { - id: '', - params: { - resmsgid: '', - msgid: '', - err: '', - status: '', - errmsg: '' - }, - responseCode: '200', - result: { - }, - ts: '', - ver: '', - headers: {} - } - }; - \ No newline at end of file diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts index 4b091e3a1..c8ae5e3e4 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -8,7 +8,7 @@ import { FormsModule } from '@angular/forms'; import { EditorService } from '../../services/editor/editor.service'; import { of, throwError } from 'rxjs'; import * as _ from 'lodash-es'; -import { mockData } from '../assets-browser/assets-browser.component.spec.data'; +import { mockData } from '../asset-browser/asset-browser.component.spec.data'; import { ConfigService } from '../../services/config/config.service'; import { ToasterService } from '../../services/toaster/toaster.service'; diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.data.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.data.ts deleted file mode 100644 index eca310d20..000000000 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.data.ts +++ /dev/null @@ -1,53 +0,0 @@ -export const config = { - uploadIconFormConfig: - [{ - 'code': 'name', - 'dataType': 'text', - 'editable': false, - 'inputType': 'text', - 'label': 'Asset Caption', - 'name': 'Asset Caption', - 'placeholder': 'Enter asset caption', - 'renderingHints': { - 'class': 'sb-g-col-lg-2 required' - }, - 'required': true, - 'visible': true, - 'validations': [ - { - 'type': 'required', - 'message': 'Please enter asset caption' - } - ] - }, - { - 'code': 'keywords', - 'visible': true, - 'editable': false, - 'dataType': 'list', - 'name': 'Tags', - 'placeholder': 'Add tag', - 'renderingHints': { - 'class': 'sb-g-col-lg-2' - }, - 'description': '', - 'inputType': 'keywords', - 'label': 'Tags', - 'required': true, - 'validations': [] - }, - { - 'code': 'creator', - 'dataType': 'text', - 'editable': false, - 'inputType': 'text', - 'label': 'Creator', - 'name': 'Creator', - 'placeholder': 'Enter name', - 'renderingHints': { - 'class': 'sb-g-col-lg-2' - }, - 'required': true, - 'visible': true - }] -} \ No newline at end of file From a2eeabc6fde48eb825e5f5b8a221b4e63c79b3d5 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Sat, 2 Sep 2023 11:54:16 +0530 Subject: [PATCH 10/18] removed variables --- .../lib/components/assets-browser/assets-browser.component.ts | 1 - .../src/lib/components/question/question.component.html | 2 -- 2 files changed, 3 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index daffa96f4..7166de3ee 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -15,7 +15,6 @@ import { ToasterService } from '../../services/toaster/toaster.service'; export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { @ViewChild('editor') public editorRef: ElementRef; @Output() assetDataOutput = new EventEmitter(); - @Input() editorDataInput: any; @Input() assetShow; @Input() assetType; @Input() showAssetPicker; diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.html b/projects/questionset-editor-library/src/lib/components/question/question.component.html index f73193243..d3d0e09b8 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.html +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.html @@ -40,8 +40,6 @@ > From 7a9072101aaad5974ae59a6bc53a112135fb90f8 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Mon, 4 Sep 2023 19:35:25 +0530 Subject: [PATCH 11/18] increased coverage --- .../assets-browser.component.spec.ts | 128 +++++++++++++++++- .../question/question.component.spec.data.ts | 18 +++ .../question/question.component.spec.ts | 34 ++++- 3 files changed, 175 insertions(+), 5 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts index c8ae5e3e4..9ffb1af3b 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -152,6 +152,10 @@ describe('AssetsBrowserComponent', () => { node_id: 'do_234', pre_signed_url: '/test' } + expect(component.loading).toEqual(false); + expect(component.isClosable).toEqual(true); + expect(component.assetFormValid).toEqual(false); + spyOn(component, 'uploadAndUseAsset').and.callThrough(); let questionService: QuestionService = TestBed.inject(QuestionService); let modal = true; spyOn(questionService, 'createMediaAsset').and.returnValue(of(createMediaAssetResponse)); @@ -161,11 +165,25 @@ describe('AssetsBrowserComponent', () => { spyOn(component, 'dismissPops').and.callThrough(); component.uploadAndUseAsset(modal); expect(questionService.createMediaAsset).toHaveBeenCalled(); - expect(component.loading).toEqual(true); - expect(component.isClosable).toEqual(false); - expect(component.assetFormValid).toEqual(false); }); it('#updateContentWithURL should update asset with url', async () => { + let fileURL = 'video/webm'; + let mimeType = 'video'; + let contentId = 'do_123'; + const data = new FormData(); + data.append('fileUrl', fileURL); + data.append('mimeType', mimeType); + + const conf = { + enctype: 'multipart/form-data', + processData: false, + contentType: false, + cache: false + }; + const option = { + data, + param: conf + }; const createMediaAssetResponse = mockData.serverResponse; createMediaAssetResponse.result = { node_id: 'do_123' @@ -175,10 +193,50 @@ describe('AssetsBrowserComponent', () => { node_id: 'do_234', pre_signed_url: '/test' } + spyOn(component, 'updateContentWithURL').and.callThrough(); + component.updateContentWithURL(fileURL, mimeType, contentId); let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService, 'getQuestionList').and.returnValue(throwError({})); let modal = true; spyOn(questionService, 'uploadMedia').and.returnValue(of(createMediaAssetResponse)); + component.getUploadAsset('do_123', modal); + }); + it('#updateContentWithURL should update asset with url', async () => { + let fileURL = 'video/webm'; + let mimeType = 'video'; + let contentId = 'do_123'; + const data = new FormData(); + data.append('fileUrl', fileURL); + data.append('mimeType', mimeType); + + const conf = { + enctype: 'multipart/form-data', + processData: false, + contentType: false, + cache: false + }; + const option = { + data, + param: conf + }; + const createMediaAssetResponse = mockData.serverResponse; + createMediaAssetResponse.result = { + node_id: 'do_123' + } + const preSignedResponse = mockData.serverResponse; + preSignedResponse.result = { + node_id: 'do_234', + pre_signed_url: '/test' + } + spyOn(component, 'updateContentWithURL').and.callThrough(); + component.updateContentWithURL(fileURL, mimeType, contentId); + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService, 'getQuestionList').and.returnValue(throwError({})); + spyOn(questionService, "uploadMedia").and.returnValue( + throwError("error") + ); }); + it('#getUploadAsset should get asset', async () => { const createMediaAssetResponse = mockData.serverResponse; createMediaAssetResponse.result = { @@ -189,18 +247,62 @@ describe('AssetsBrowserComponent', () => { node_id: 'do_234', pre_signed_url: '/test' } + spyOn(component, 'getUploadAsset').and.callThrough(); let questionService: QuestionService = TestBed.inject(QuestionService); - let modal = true; + let modal = undefined; spyOn(questionService, 'getVideo').and.returnValue(of(createMediaAssetResponse)); + expect(component.loading).toEqual(false); + expect(component.isClosable).toEqual(true); + expect(component.assetFormValid).toEqual(false); + spyOn(component, 'addAssetInEditor').and.callThrough(); + component.addAssetInEditor(modal); }); + it('#getUploadAsset should get asset', async () => { + const createMediaAssetResponse = mockData.serverResponse; + createMediaAssetResponse.result = { + node_id: 'do_123' + } + const preSignedResponse = mockData.serverResponse; + preSignedResponse.result = { + node_id: 'do_234', + pre_signed_url: '/test' + } + }); + + it('#addAssetInEditor() should emit proper event', () => { let modal = undefined; + spyOn(component, 'addAssetInEditor').and.callThrough(); + // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); + component.addAssetInEditor(modal); + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + }); + it('#addAssetInEditor() should emit proper event', () => { let modal = undefined; + component.url = '/test'; spyOn(component, 'addAssetInEditor').and.callThrough(); // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); component.addAssetInEditor(modal); // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); }); + it('#getMediaOriginURL() should emit media origin url', () => { + let src = '/test'; + spyOn(component, 'getMediaOriginURL').and.callThrough(); + // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); + component.getMediaOriginURL(src); + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + }); + it('#getMediaOriginURL() should emit media origin url', () => { + let url = '/test'; + spyOn(component, 'getMediaOriginURL').and.callThrough(); + const src = 'https://example.com/image.jpg'; + + const result = component.getMediaOriginURL(src); + + expect(result).toEqual(src); // No replacement should occur + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + }); + it('#uploadAsset() should create asset on API success', () => { const file = new File([''], 'filename', { type: 'video' }); const event = { @@ -293,6 +395,23 @@ describe('AssetsBrowserComponent', () => { expect(component.showAssetUploadModal).toBeFalsy(); }); + it('#initiateAssetUploadModal() should set showAssetUploadModal to false', () => { + spyOn(component, 'initiateAssetUploadModal').and.callThrough(); + component.initiateAssetUploadModal(); + expect(component.showAssetPicker).toBeFalsy(); + expect(component.showAssetUploadModal).toBeTruthy(); + expect(component.loading).toBeFalsy(); + expect(component.isClosable).toBeTruthy(); + }); + + it('#resetFormConfig() should reset form', () => { + spyOn(component, 'resetFormConfig').and.callThrough(); + component.resetFormConfig(); + expect(component.assetUploadLoader).toBeFalsy(); + expect(component.assetFormValid).toBeFalsy(); + component.formConfig = component.initialFormConfig; + }); + it('#lazyloadMyAssets() should get my assets', () => { spyOn(component, 'getMyAssets'); component.lazyloadMyAssets(); @@ -332,6 +451,7 @@ it('#searchAsset() should call getMyAssets for my videos', () => { expect(component.searchMyInput).toEqual(''); expect(component.getMyAssets).toHaveBeenCalledWith(0, '', true); }); + it('#searchAsset() should call allVideos for all videos ', () => { spyOn(component, 'getAllAssets'); component.searchAsset('clearInput', 'allAssets'); diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts index bddc88aef..87f48783c 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts @@ -3406,6 +3406,24 @@ export const videoSolutionObject= { "value": "do_2137972441518325761398" }; +export const audioSolutionObject= { + "id": "4772d9da-569f-46bb-a8b1-9faf742d0640", + "type": "audio", + "value": "do_2137972441518325761398" +}; + +export const mediaAudioArray = [ + { + "id": "do_2137972441518325761398", + "src": "/assets/public/content/assets/do_2137972441518325761398/earth.mp3", + "type": "audio", + "assetId": "do_2137972441518325761398", + "name": "earth", + "baseUrl": "https://dev.inquiry.sunbird.org", + "thubmnail": "/assets/public/content/assets/do_21379724415183257613675/earth.png" + } +]; + export const mediaVideoArray = [ { "id": "do_2137972441518325761398", diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts index 9e77e6a06..6e2b2d335 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts @@ -27,7 +27,9 @@ import { interactionChoiceEditorState, RubricData, videoSolutionObject, - mediaVideoArray + mediaVideoArray, + audioSolutionObject, + mediaAudioArray } from "./question.component.spec.data"; import { of, throwError } from "rxjs"; import * as urlConfig from "../../services/config/url.config.json"; @@ -753,6 +755,16 @@ describe("QuestionComponent", () => { expect(solution).toBeDefined(); }) + it('#getQuestionSolution() should return audio solution', () => { + component.mediaArr = mediaAudioArray; + component.selectedSolutionType = "audio"; + spyOn(component, 'getQuestionSolution').and.callThrough(); + spyOn(component, 'getMediaById').and.callThrough(); + spyOn(component, 'getAssetSolutionHtml').and.callThrough(); + const solution = component.getQuestionSolution(audioSolutionObject); + expect(solution).toBeDefined(); + }) + it('#getQuestionSolution() should return html solution', () => { const solutionObject = { "id": "4772d9da-569f-46bb-a8b1-9faf742d0640", @@ -771,6 +783,13 @@ describe("QuestionComponent", () => { expect(mediaobj).toBeDefined(); }); + it('#getMediaById() should return audio object', () => { + component.mediaArr = mediaAudioArray; + spyOn(component, 'getMediaById').and.callThrough(); + const mediaobj = component.getMediaById(mediaAudioArray[0].id); + expect(mediaobj).toBeDefined(); + }); + it('#getAssetSolutionHtml() should return assetSolutionHtml', () => { spyOn(component, 'getAssetSolutionHtml').and.callThrough(); component.selectedSolutionType = "video"; @@ -778,6 +797,13 @@ describe("QuestionComponent", () => { expect(assetSolutionHtml).toBeDefined(); }); + it('#getAssetSolutionHtml() should return assetSolutionHtml', () => { + spyOn(component, 'getAssetSolutionHtml').and.callThrough(); + component.selectedSolutionType = "audio"; + const assetSolutionHtml = component.getAssetSolutionHtml(mediaAudioArray[0].thubmnail, mediaAudioArray[0].src, mediaAudioArray[0].id); + expect(assetSolutionHtml).toBeDefined(); + }); + it("call #getMcqQuestionHtmlBody() to verify questionBody", () => { const question = '
{question}
'; const templateId = "mcq-vertical"; @@ -1344,6 +1370,12 @@ describe("QuestionComponent", () => { component.deleteSolution(); expect(component.mediaArr).toBeDefined(); }); + it("#deleteSolution() should call deleteSolution and define mediaArr for audio type", () => { + component.editorState = mockData.editorState; + component.selectedSolutionType = "audio"; + component.deleteSolution(); + expect(component.mediaArr).toBeDefined(); + }); it("#validateQuestionData() should call validateQuestionData and question is undefined", () => { component.editorState = mockData.editorState; component.editorState.question = undefined; From 425780a1939dd0a1a041b46218cb8dc918b18823 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Tue, 5 Sep 2023 02:46:09 +0530 Subject: [PATCH 12/18] increased coverage --- .../assets-browser.component.spec.ts | 187 ++++++++++++++++-- .../assets-browser.component.ts | 7 +- 2 files changed, 170 insertions(+), 24 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts index 9ffb1af3b..ed59b517f 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -30,7 +30,7 @@ const mockEditorService = { channel: 'sunbird' } }, - + apiErrorHandling: () => {}, appendCloudStorageHeaders: (config) => { return {...config, headers: {'x-ms-blob-type': 'BlockBlob'}}; } @@ -119,6 +119,24 @@ describe('AssetsBrowserComponent', () => { expect(component.assetsCount).toEqual(1); }); + it('#getMyAssets() should return assets on API success', + async () => { + const response = mockData.serverResponse; + response.result = { + count: 1, + content: [{ + downloadUrl: '/test' + }] + } + component.assetType = "video"; + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService, 'getAssetMedia').and.returnValue(of(response)); + const offset = 0; + const query = "test"; + component.getMyAssets(offset, query); + expect(component.assetsCount).toEqual(1); + }); + it('#getAllAssets() should return assets on API success', async () => { const response = mockData.serverResponse; response.result = { @@ -135,6 +153,27 @@ describe('AssetsBrowserComponent', () => { expect(component.assetsCount).toEqual(1); }); + it('#getAllAssets() should return assets on API success', async () => { + const response = mockData.serverResponse; + response.result = { + count: 1, + content: [{ + downloadUrl: '/test' + }] + } + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService, 'getAssetMedia').and.returnValue(of(response)); + const offset = 0; + const query = "test"; + component.getAllAssets(offset, query); + let modal = undefined; + spyOn(component, 'addAssetInEditor').and.callThrough(); + // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); + component.addAssetInEditor(modal); + spyOn(component.allAssets, 'push'); + expect(component.assetsCount).toEqual(1); + }); + it('#resetFormData() should reset the form data', () => { component.resetFormData(); expect(component.assetUploadLoader).toEqual(false); @@ -268,21 +307,44 @@ describe('AssetsBrowserComponent', () => { node_id: 'do_234', pre_signed_url: '/test' } + spyOn(component, 'getUploadAsset').and.callThrough(); + let questionService: QuestionService = TestBed.inject(QuestionService); + let editorService: EditorService = TestBed.inject(EditorService); + const mockModal = {}; // You can create a mock modal as needed + spyOn(questionService, 'getVideo').and.returnValue(of(createMediaAssetResponse)); + spyOn(editorService, 'apiErrorHandling').and.callFake(() => {}); + component.getUploadAsset('assetId', mockModal); + + expect(questionService.getVideo).toHaveBeenCalledWith('assetId'); }); - it('#addAssetInEditor() should emit proper event', () => { let modal = undefined; - spyOn(component, 'addAssetInEditor').and.callThrough(); - // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); - component.addAssetInEditor(modal); - // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); - }); + // it('#addAssetInEditor() should emit proper event', () => { const videoModal = { + // deny: jasmine.createSpy('deny'), + // }; + // spyOn(component, 'addAssetInEditor').and.callThrough(); + // // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); + // component.addAssetInEditor(videoModal); + // // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + // }); - it('#addAssetInEditor() should emit proper event', () => { let modal = undefined; - component.url = '/test'; + + it('#addAssetInEditor() should add asset and emit data', () => { + const videoModal = { + deny: jasmine.createSpy('deny'), + }; + const assetUrl = 'testAssetUrl'; + const assetId = 'testAssetId'; + const assetName = 'testAssetName'; spyOn(component, 'addAssetInEditor').and.callThrough(); - // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); - component.addAssetInEditor(modal); - // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + component.addAssetInEditor(videoModal, assetUrl, assetId, assetName); + expect(component.showAssetPicker).toBe(false); + // spyOn(component.assetDataOutput, 'emit'); + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith({ + // downloadUrl: 'testAssetUrl', + // src: 'mockMediaOriginURL', + // thumbnail: null, + // }); + expect(videoModal.deny).toHaveBeenCalled(); }); it('#getMediaOriginURL() should emit media origin url', () => { @@ -292,15 +354,46 @@ describe('AssetsBrowserComponent', () => { component.getMediaOriginURL(src); // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); }); - it('#getMediaOriginURL() should emit media origin url', () => { - let url = '/test'; - spyOn(component, 'getMediaOriginURL').and.callThrough(); - const src = 'https://example.com/image.jpg'; + it('#getMediaOriginURL() should replace cloud storage URL with assetProxyUrl', () => { + component.assetProxyUrl = 'https://asset-proxy.com/'; + editorService.editorConfig.context.cloudStorageUrls = [ + 'https://storage-url1.com/', + 'https://storage-url2.com/' + ]; + const src = 'https://storage-url1.com/video.mp3'; + + // Act + const result = component.getMediaOriginURL(src); + + // Assert + expect(result).toEqual('https://asset-proxy.com/video.mp3'); + }); + + it('#getMediaOriginURL() should handle no matches', () => { + component.assetProxyUrl = 'https://asset-proxy.com/'; + editorService.editorConfig.context.cloudStorageUrls = [ + 'https://storage-url1.com/', + 'https://storage-url2.com/' + ]; + const src = 'https://unrelated-url.com/video.mp3'; + // Act const result = component.getMediaOriginURL(src); - expect(result).toEqual(src); // No replacement should occur - // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + // Assert + expect(result).toEqual('https://unrelated-url.com/video.mp3'); + }); + + it('#getMediaOriginURL() should handle empty cloudStorageUrls', () => { + component.assetProxyUrl = 'https://asset-proxy.com/'; + editorService.editorConfig.context.cloudStorageUrls = []; + const src = 'https://storage-url1.com/video.mp3'; + + // Act + const result = component.getMediaOriginURL(src); + + // Assert + expect(result).toEqual('https://storage-url1.com/video.mp3'); }); it('#uploadAsset() should create asset on API success', () => { @@ -325,6 +418,8 @@ describe('AssetsBrowserComponent', () => { expect(component.assetFormValid).toEqual(true); }) + + it('#generateAssetCreateRequest() should return asset create request', () => { let fileName = 'test'; let fileType = 'video/webm'; @@ -340,6 +435,47 @@ describe('AssetsBrowserComponent', () => { }) }); + it('should handle a valid file upload', () => { + // Prepare a mock event + const event = { + target: { + files: [new File(['test-content'], 'filename.mp3', { type: 'video' })] + } + } as any; + + component.assetType = 'video'; // Replace with your asset type + component.assetConfig = { + video: { + size: 50, + sizeType: 'MB' + } + }; + + component.uploadAsset(event); + + expect(component.assetFile).toBeTruthy(); + expect(component.assetName).toBe('filename.mp3'); + }); + + it('should handle an invalid file type', () => { + const event = { + target: { + files: [new File(['test-content'], 'test-file.exe', { type: 'txt' })] + } + } as any; + component.assetType = 'video'; + component.assetConfig = { + video: { + size: 50, + sizeType: 'MB' + } + }; + + component.uploadAsset(event); + + expect(component.showErrorMsg).toBe(true); + }); + xit('#uploadAndUseAsset should upload asset and call upload to blob', async () => { const createMediaAssetResponse = mockData.serverResponse; @@ -453,12 +589,27 @@ it('#searchAsset() should call getMyAssets for my videos', () => { }); it('#searchAsset() should call allVideos for all videos ', () => { + spyOn(component, 'getAllAssets'); component.searchAsset('clearInput', 'allAssets'); expect(component.query).toEqual(''); expect(component.searchAllInput).toEqual(''); expect(component.getAllAssets).toHaveBeenCalledWith(0, '', true); }); + +it('#searchAsset() should call getMyAssets for my videos', () => { + // spyOn(component, 'getMyAssets'); + const event = { + target: { + value:"testing" + } + } + component.searchAsset(event, 'myAssets'); + expect(component.query).toEqual('testing'); + expect(component.searchMyInput).toEqual(''); + // expect(component.getMyAssets).toHaveBeenCalledWith(0, '', true); +}); + it('#ngOnInit() should call ngOnInit and define formConfig', () => { component.assetType = "video"; component.assetConfig = { diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index 7166de3ee..980acdc76 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -324,12 +324,7 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { this.loading = false; this.isClosable = true; } - - public isEditorReadOnly(state) { - this.editorInstance.isReadOnly = state; - this.isAssetBrowserReadOnly = state; - } - + uploadAsset(event) { this.assetFile = event.target.files[0]; this.assetName = this.assetFile.name; From d2f5f2de382cb29b2f34ed941a92adeb0d55ba1e Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Tue, 5 Sep 2023 09:58:54 +0530 Subject: [PATCH 13/18] coverage --- .../assets-browser.component.spec.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts index ed59b517f..f829b6af0 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -318,20 +318,11 @@ describe('AssetsBrowserComponent', () => { expect(questionService.getVideo).toHaveBeenCalledWith('assetId'); }); - // it('#addAssetInEditor() should emit proper event', () => { const videoModal = { - // deny: jasmine.createSpy('deny'), - // }; - // spyOn(component, 'addAssetInEditor').and.callThrough(); - // // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); - // component.addAssetInEditor(videoModal); - // // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); - // }); - - it('#addAssetInEditor() should add asset and emit data', () => { const videoModal = { deny: jasmine.createSpy('deny'), }; + component.selectedAsset = {ownershipType: Array(1), code: '652144fc-cd68-4259-a730-a5acb1983a8f', keywords: '', channel: '01309282781705830427', downloadUrl: 'https://sunbirddevbbpublic.blob.core.windows.net/s…81612/screencast-from-25-08-23-040945-pm-ist.webm'}; const assetUrl = 'testAssetUrl'; const assetId = 'testAssetId'; const assetName = 'testAssetName'; @@ -339,11 +330,7 @@ describe('AssetsBrowserComponent', () => { component.addAssetInEditor(videoModal, assetUrl, assetId, assetName); expect(component.showAssetPicker).toBe(false); // spyOn(component.assetDataOutput, 'emit'); - // expect(component.assetDataOutput.emit).toHaveBeenCalledWith({ - // downloadUrl: 'testAssetUrl', - // src: 'mockMediaOriginURL', - // thumbnail: null, - // }); + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(); expect(videoModal.deny).toHaveBeenCalled(); }); From ec46d5993dc49076f8c74b926a0cc35be94c00d7 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Tue, 5 Sep 2023 10:30:04 +0530 Subject: [PATCH 14/18] coverage --- .../assets-browser.component.spec.ts | 130 ++++++++---------- 1 file changed, 58 insertions(+), 72 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts index f829b6af0..c459d44c3 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -57,7 +57,6 @@ describe('AssetsBrowserComponent', () => { component.assetType = "video"; fixture.detectChanges(); }) - it('should create', () => { expect(component).toBeTruthy(); }); @@ -136,7 +135,7 @@ describe('AssetsBrowserComponent', () => { component.getMyAssets(offset, query); expect(component.assetsCount).toEqual(1); }); - + it('#getAllAssets() should return assets on API success', async () => { const response = mockData.serverResponse; response.result = { @@ -247,7 +246,6 @@ describe('AssetsBrowserComponent', () => { const data = new FormData(); data.append('fileUrl', fileURL); data.append('mimeType', mimeType); - const conf = { enctype: 'multipart/form-data', processData: false, @@ -275,7 +273,6 @@ describe('AssetsBrowserComponent', () => { throwError("error") ); }); - it('#getUploadAsset should get asset', async () => { const createMediaAssetResponse = mockData.serverResponse; createMediaAssetResponse.result = { @@ -307,31 +304,20 @@ describe('AssetsBrowserComponent', () => { node_id: 'do_234', pre_signed_url: '/test' } - spyOn(component, 'getUploadAsset').and.callThrough(); - let questionService: QuestionService = TestBed.inject(QuestionService); - let editorService: EditorService = TestBed.inject(EditorService); - const mockModal = {}; // You can create a mock modal as needed - spyOn(questionService, 'getVideo').and.returnValue(of(createMediaAssetResponse)); - spyOn(editorService, 'apiErrorHandling').and.callFake(() => {}); - component.getUploadAsset('assetId', mockModal); - - expect(questionService.getVideo).toHaveBeenCalledWith('assetId'); }); + it('#addAssetInEditor() should emit proper event', () => { let modal = undefined; + spyOn(component, 'addAssetInEditor').and.callThrough(); + // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); + component.addAssetInEditor(modal); + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + }); - it('#addAssetInEditor() should add asset and emit data', () => { - const videoModal = { - deny: jasmine.createSpy('deny'), - }; - component.selectedAsset = {ownershipType: Array(1), code: '652144fc-cd68-4259-a730-a5acb1983a8f', keywords: '', channel: '01309282781705830427', downloadUrl: 'https://sunbirddevbbpublic.blob.core.windows.net/s…81612/screencast-from-25-08-23-040945-pm-ist.webm'}; - const assetUrl = 'testAssetUrl'; - const assetId = 'testAssetId'; - const assetName = 'testAssetName'; + it('#addAssetInEditor() should emit proper event', () => { let modal = undefined; + component.url = '/test'; spyOn(component, 'addAssetInEditor').and.callThrough(); - component.addAssetInEditor(videoModal, assetUrl, assetId, assetName); - expect(component.showAssetPicker).toBe(false); - // spyOn(component.assetDataOutput, 'emit'); - // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(); - expect(videoModal.deny).toHaveBeenCalled(); + // spyOn(component.assetDataOutput, 'emit').and.callFake(() => {}); + component.addAssetInEditor(modal); + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); }); it('#getMediaOriginURL() should emit media origin url', () => { @@ -341,6 +327,17 @@ describe('AssetsBrowserComponent', () => { component.getMediaOriginURL(src); // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); }); + it('#getMediaOriginURL() should emit media origin url', () => { + let url = '/test'; + spyOn(component, 'getMediaOriginURL').and.callThrough(); + const src = 'https://example.com/image.jpg'; + + const result = component.getMediaOriginURL(src); + + expect(result).toEqual(src); // No replacement should occur + // expect(component.assetDataOutput.emit).toHaveBeenCalledWith(mockData.assetBrowserEvent); + }); + it('#getMediaOriginURL() should replace cloud storage URL with assetProxyUrl', () => { component.assetProxyUrl = 'https://asset-proxy.com/'; editorService.editorConfig.context.cloudStorageUrls = [ @@ -363,14 +360,11 @@ describe('AssetsBrowserComponent', () => { 'https://storage-url2.com/' ]; const src = 'https://unrelated-url.com/video.mp3'; - - // Act - const result = component.getMediaOriginURL(src); - - // Assert - expect(result).toEqual('https://unrelated-url.com/video.mp3'); + // Assert + const result = component.getMediaOriginURL(src); + expect(result).toEqual('https://unrelated-url.com/video.mp3'); }); - + it('#getMediaOriginURL() should handle empty cloudStorageUrls', () => { component.assetProxyUrl = 'https://asset-proxy.com/'; editorService.editorConfig.context.cloudStorageUrls = []; @@ -404,9 +398,6 @@ describe('AssetsBrowserComponent', () => { expect(component.assetUploadLoader).toEqual(true); expect(component.assetFormValid).toEqual(true); }) - - - it('#generateAssetCreateRequest() should return asset create request', () => { let fileName = 'test'; let fileType = 'video/webm'; @@ -422,6 +413,36 @@ describe('AssetsBrowserComponent', () => { }) }); + xit('#uploadAndUseAsset should upload asset and call upload to blob', + async () => { + const createMediaAssetResponse = mockData.serverResponse; + createMediaAssetResponse.result = { + node_id: 'do_123' + } + const preSignedResponse = mockData.serverResponse; + preSignedResponse.result = { + node_id: 'do_234', + pre_signed_url: '/test?' + } + const uploadMediaResponse = mockData.serverResponse; + uploadMediaResponse.result = { + node_id: 'do_234', + content_url: '/test' + } + component.showAssetUploadModal = false; + let questionService: QuestionService = TestBed.inject(QuestionService); + let modal = true; + spyOn(questionService, 'createMediaAsset').and.returnValue(of(createMediaAssetResponse)); + spyOn(component, 'uploadToBlob').and.returnValue(of(true)); + spyOn(questionService, 'uploadMedia').and.returnValue(of(uploadMediaResponse)); + spyOn(component, 'addAssetInEditor').and.callThrough(); + spyOn(component, 'dismissPops').and.callFake(()=> {}); + spyOn(component, 'uploadAndUseAsset').and.callThrough(); + component.uploadAndUseAsset(modal); + expect(questionService.createMediaAsset).toHaveBeenCalled(); + expect(questionService.generatePreSignedUrl).toHaveBeenCalled(); + expect(component.uploadToBlob).toHaveBeenCalled(); + }); it('should handle a valid file upload', () => { // Prepare a mock event const event = { @@ -462,38 +483,6 @@ describe('AssetsBrowserComponent', () => { expect(component.showErrorMsg).toBe(true); }); - - xit('#uploadAndUseAsset should upload asset and call upload to blob', - async () => { - const createMediaAssetResponse = mockData.serverResponse; - createMediaAssetResponse.result = { - node_id: 'do_123' - } - const preSignedResponse = mockData.serverResponse; - preSignedResponse.result = { - node_id: 'do_234', - pre_signed_url: '/test?' - } - const uploadMediaResponse = mockData.serverResponse; - uploadMediaResponse.result = { - node_id: 'do_234', - content_url: '/test' - } - component.showAssetUploadModal = false; - let questionService: QuestionService = TestBed.inject(QuestionService); - let modal = true; - spyOn(questionService, 'createMediaAsset').and.returnValue(of(createMediaAssetResponse)); - spyOn(component, 'uploadToBlob').and.returnValue(of(true)); - spyOn(questionService, 'uploadMedia').and.returnValue(of(uploadMediaResponse)); - spyOn(component, 'addAssetInEditor').and.callThrough(); - spyOn(component, 'dismissPops').and.callFake(()=> {}); - spyOn(component, 'uploadAndUseAsset').and.callThrough(); - component.uploadAndUseAsset(modal); - expect(questionService.createMediaAsset).toHaveBeenCalled(); - expect(questionService.generatePreSignedUrl).toHaveBeenCalled(); - expect(component.uploadToBlob).toHaveBeenCalled(); - }); - it('#uploadToBlob() should upload blob on API success', () => { let signedURL = '/test'; let file = new File([], 'fileName'); @@ -540,7 +529,6 @@ describe('AssetsBrowserComponent', () => { component.lazyloadMyAssets(); expect(component.getMyAssets).toHaveBeenCalledWith(0, undefined, true); }); - it('#lazyloadAllAssets() should get all assets', () => { spyOn(component, 'getAllAssets'); component.lazyloadAllAssets(); @@ -576,7 +564,6 @@ it('#searchAsset() should call getMyAssets for my videos', () => { }); it('#searchAsset() should call allVideos for all videos ', () => { - spyOn(component, 'getAllAssets'); component.searchAsset('clearInput', 'allAssets'); expect(component.query).toEqual(''); @@ -596,7 +583,6 @@ it('#searchAsset() should call getMyAssets for my videos', () => { expect(component.searchMyInput).toEqual(''); // expect(component.getMyAssets).toHaveBeenCalledWith(0, '', true); }); - it('#ngOnInit() should call ngOnInit and define formConfig', () => { component.assetType = "video"; component.assetConfig = { @@ -682,4 +668,4 @@ it('#dismissPops() should close both pops ', () => { expect(modal.deny).toHaveBeenCalled(); }); -}); +}); \ No newline at end of file From 6867dd0c09dcb4c0712e8fdd980401b966b27406 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Tue, 5 Sep 2023 12:37:21 +0530 Subject: [PATCH 15/18] increase coverage --- .../assets-browser.component.spec.ts | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts index c459d44c3..a8aadb1de 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.spec.ts @@ -172,6 +172,12 @@ describe('AssetsBrowserComponent', () => { spyOn(component.allAssets, 'push'); expect(component.assetsCount).toEqual(1); }); + it('should handle API error gracefully', () => { + const mockError = { status: 500, message: 'Server Error' }; + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService, 'getAssetMedia').and.returnValue(throwError(mockError)); + spyOn(editorService, "apiErrorHandling").and.callFake(() => {}); + }); it('#resetFormData() should reset the form data', () => { component.resetFormData(); @@ -237,7 +243,28 @@ describe('AssetsBrowserComponent', () => { spyOn(questionService, 'getQuestionList').and.returnValue(throwError({})); let modal = true; spyOn(questionService, 'uploadMedia').and.returnValue(of(createMediaAssetResponse)); - component.getUploadAsset('do_123', modal); + component.getUploadAsset(createMediaAssetResponse.result['node_id'], modal); + }); + it('should handle error correctly', () => { + const fileURL = 'mockFileURL'; + const mimeType = 'mockMimeType'; + const contentId = 'mockContentId'; + const modal = 'mockModal'; + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService, 'uploadMedia').and.returnValue( + throwError({ message: 'Mock error' }) + ); + let configService: ConfigService = TestBed.inject(ConfigService); + spyOn(configService, 'labelConfig').and.returnValue({ messages: { error: { '027': 'MockErrorMessage' } } }); + + spyOn(editorService, "apiErrorHandling").and.callFake(() => {}); + + component.updateContentWithURL(fileURL, mimeType, contentId, modal); + + expect(questionService.uploadMedia).toHaveBeenCalledOnceWith(jasmine.anything(), contentId); + expect(component.isClosable).toBe(true); + expect(component.loading).toBe(false); + expect(component.assetFormValid).toBe(true); }); it('#updateContentWithURL should update asset with url', async () => { let fileURL = 'video/webm'; @@ -494,6 +521,36 @@ describe('AssetsBrowserComponent', () => { }) }); + it('should upload to blob and return data', () => { + const signedURL = 'mockedSignedURL'; + const file = 'mockedFile'; + const config = {}; + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService.http, 'put').and.returnValue(of({ mockData: 'data' })); + + component.uploadToBlob(signedURL, file, config).subscribe((data) => { + expect(data).toEqual({ mockData: 'data' }); // Assert the expected response + }); + }); + + it('should handle API error and throw an error', () => { + const signedURL = 'mockedSignedURL'; + const file = 'mockedFile'; + const config = {}; + let questionService: QuestionService = TestBed.inject(QuestionService); + spyOn(questionService.http, 'put').and.returnValue(throwError({ errorMessage: 'API error' })); + + component.uploadToBlob(signedURL, file, config).subscribe( + () => { + // This should not be called since it's an error case + fail('Expected error to be thrown'); + }, + (error) => { + spyOn(editorService, "apiErrorHandling").and.callFake(() => {}); + } + ); + }); + it('#dismissAssetUploadModal() should set showAssetPicker to true', () => { component.showAssetPicker = true; spyOn(component, 'dismissAssetUploadModal').and.callThrough(); From 440cc139fb6a64b15cd9d1a629c2ee8477fd21ec Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Tue, 5 Sep 2023 15:21:52 +0530 Subject: [PATCH 16/18] resolved code duplicacy --- .../assets-browser.component.ts | 61 ------------------- 1 file changed, 61 deletions(-) diff --git a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts index 980acdc76..6a7a42817 100644 --- a/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/assets-browser/assets-browser.component.ts @@ -79,67 +79,6 @@ export class AssetsBrowserComponent implements OnInit, OnChanges, OnDestroy { this.astSizeType = this.assetConfig[this.assetType].sizeType; this.acceptedFileType = this.assetConfig[this.assetType].accepted; this.acceptAssetType = this.getAcceptType(this.assetConfig[this.assetType].accepted, this.assetType); - this.editorConfig = { - toolbar: ['heading', '|', 'bold', '|', 'italic', '|', 'underline', '|', 'BulletedList', '|', 'alignment', - '|', 'insertTable', '|', 'numberedList', '|', 'fontSize', '|', 'subscript', '|', 'superscript', '|', - 'MathText', '|', 'specialCharacters', '|' - ], - fontSize: { - options: [ - 'eight', - 'ten', - 'twelve', - 'fourteen', - 'sixteen', - 'eighteen', - 'twenty', - 'twentytwo', - 'twentyfour', - 'twentysix', - 'twentyeight', - 'thirty', - 'thirtysix' - ] - }, - image: { - resizeUnit: '%', - resizeOptions: [{ - name: 'resizeImage:25', - value: '25', - icon: 'small', - className: 'resize-25' - }, - { - name: 'resizeImage:50', - value: '50', - icon: 'medium', - className: 'resize-50' - }, - { - name: 'resizeImage:75', - value: '75', - icon: 'large', - className: 'resize-75' - }, - { - name: 'resizeImage:100', - value: '100', - icon: 'full', - className: 'resize-100' - }, - { - name: 'resizeImage:original', - value: null, - icon: 'original', - className: 'resize-original' - }], - toolbar: ['imageStyle:alignLeft', 'imageStyle:alignCenter', 'imageStyle:alignRight', '|', - 'resizeImage:25', 'resizeImage:50', 'resizeImage:75', 'resizeImage:100', 'resizeImage:original'], - styles: ['full', 'alignLeft', 'alignRight', 'alignCenter'] - }, - isReadOnly: false, - removePlugins: ['ImageCaption', 'mathtype', 'ChemType', 'ImageResizeHandles'] - }; } ngOnChanges() { From c6d49ce4e7e710bbf250942b4a41a1f891eeb14f Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Wed, 6 Sep 2023 17:19:39 +0530 Subject: [PATCH 17/18] Re-run Test cases From f46e295742bf42afe581ca6434c5f1ffa5fe9457 Mon Sep 17 00:00:00 2001 From: Simran Nigam Date: Wed, 6 Sep 2023 18:24:56 +0530 Subject: [PATCH 18/18] Re-run Test cases