diff --git a/addons/website_slides/static/src/js/slides_upload.js b/addons/website_slides/static/src/js/slides_upload.js index 9601dedaab1..88c5cce28d9 100644 --- a/addons/website_slides/static/src/js/slides_upload.js +++ b/addons/website_slides/static/src/js/slides_upload.js @@ -570,16 +570,13 @@ var SlideUploadDialog = Dialog.extend({ if (isImage) { preview.src = dataURL; } - this.file.data = dataURL.split(',', 1)[1]; + this.file.data = dataURL.split(',', 2)[1]; this._showPreviewColumn(); }); } else { - var ArrayReader = new FileReader(); this.set('can_submit_form', false); - // file read as ArrayBuffer for pdfjsLib get_Document API - ArrayReader.readAsArrayBuffer(file); - ArrayReader.onload = async function (evt) { - var buffer = evt.target.result; + utils.getDataURLFromFile(file).then(async dataURL => { + this.file.data = dataURL.split(',', 2)[1]; /** * The following line fixes pdfjsLib 'Util' global variable. * This is (most likely) related to #32181 which lazy loads most assets. @@ -598,7 +595,8 @@ var SlideUploadDialog = Dialog.extend({ * cause much harm. */ window.Util = window.pdfjsLib.Util; - const pdfTask = window.pdfjsLib.getDocument(new Uint8Array(buffer)); + // pdf is stored in file.data in base64 and converted in binary (atob) to generate the preview + const pdfTask = window.pdfjsLib.getDocument({ data: atob(this.file.data) }); pdfTask.onPassword = () => { this._alertDisplay(_t("You can not upload password protected file.")); this._fileReset(); @@ -608,7 +606,7 @@ var SlideUploadDialog = Dialog.extend({ const pdf = await pdfTask.promise; self._formSetFieldValue('duration', (pdf.numPages || 0) * 5); - const page = await pdf.getPage(1) + const page = await pdf.getPage(1); var viewport = page.getViewport({scale: 1}); var canvas = document.getElementById('data_canvas'); var context = canvas.getContext('2d'); @@ -625,7 +623,7 @@ var SlideUploadDialog = Dialog.extend({ } loaded = true; self._showPreviewColumn(); - }; + }); } if (!preventOnchange) { diff --git a/addons/website_slides/static/tests/tours/slide_course_publisher_standard.js b/addons/website_slides/static/tests/tours/slide_course_publisher_standard.js index dcc3b42c85e..d11af119019 100644 --- a/addons/website_slides/static/tests/tours/slide_course_publisher_standard.js +++ b/addons/website_slides/static/tests/tours/slide_course_publisher_standard.js @@ -98,7 +98,9 @@ wTourUtils.registerWebsitePreviewTour('course_publisher_standard', { }, { content: "eLearning: use breadcrumb to go back to channel", trigger: 'iframe .o_wslides_course_nav a:contains("Déboulonnate")', -}] +}], + slidesTourTools.addImageToSection('Introduction', 'Overview', true), + slidesTourTools.addPdfToSection('Introduction', 'Exercise', true), // [ // { // content: 'eLearning: move new course inside introduction', diff --git a/addons/website_slides/static/tests/tours/slides_tour_tools.js b/addons/website_slides/static/tests/tours/slides_tour_tools.js index 40091bee236..d602a9d9c6d 100644 --- a/addons/website_slides/static/tests/tours/slides_tour_tools.js +++ b/addons/website_slides/static/tests/tours/slides_tour_tools.js @@ -1,5 +1,13 @@ /** @odoo-module **/ +import utils from 'web.utils'; + +/* + * Constant + */ +const testPngImage = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAD0lEQVQIHQEEAPv/AIdaewLIAV0IjhGPAAAAAElFTkSuQmCC'; +const testPdf = 'JVBERi0xLjEKJWPDtsO2bW1lbnQKMSAwIG9iago8PC9UeXBlIC9DYXRhbG9nCi9QYWdlcyAyIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PC9UeXBlIC9QYWdlcwovS2lkcyBbMyAwIFJdCi9Db3VudCAxCi9NZWRpYUJveCBbMCAwIDIxMCAyOTddCj4+CmVuZG9iagozIDAgb2JqCjw8L1R5cGUgL1BhZ2UKL1BhcmVudCAyIDAgUgovUmVzb3VyY2VzCjw8L0ZvbnQKPDwvRjEKPDwvVHlwZSAvRm9udAovU3VidHlwZSAvVHlwZTEKL0Jhc2VGb250IC9UaW1lcy1Sb21hbgo+Pgo+Pgo+PgovQ29udGVudHMgNCAwIFIKPj4KZW5kb2JqCjQgMCBvYmoKPDwvTGVuZ3RoIDQ4Pj4Kc3RyZWFtCkJUCi9GMSA0OCBUZgo1MCAxMzAgVGQKKFRFU1QpIFRqCkVUCmVuZHN0cmVhbQplbmRvYmoKeHJlZgowIDUKMDAwMDAwMDAwMCA2NTUzNSBmCjAwMDAwMDAwMjEgMDAwMDAgbgowMDAwMDAwMDY5IDAwMDAwIG4KMDAwMDAwMDE0OSAwMDAwMCBuCjAwMDAwMDAyOTggMDAwMDAgbgp0cmFpbGVyCjw8L1Jvb3QgMSAwIFIKL1NpemUgNQo+PgpzdGFydHhyZWYKMzgxCiUlRU9G'; + /* * PUBLISHER / CONTENT CREATION */ @@ -87,6 +95,128 @@ var addArticleToSection = function (sectionName, pageName, backend) { }]; }; +const fillInFileInput = async (input, name, type, content) => { + const blob = await (await fetch(`data:${type};base64,${content}`)).blob(); + const file = new File([blob], name, { type: type}); + const dataTransfer = new DataTransfer(); + dataTransfer.items.add(file); + input.files = dataTransfer.files; + input.dispatchEvent(new Event('change', { bubbles: true })); +}; + +const compareBase64Content = async (url, name, type, expectedContent) => { + const blob = await (await fetch(url)).blob(); + const file = new File([blob], name, { type: type}); + const actualContent = await utils.getDataURLFromFile(file).then(dataURL => dataURL.split(',', 2)[1]); + return expectedContent === actualContent; +}; + +/** + * Test the upload of an image file as a new content and especially the binary content of the uploaded image. + */ +const addImageToSection = (sectionName, pageName, backend) => { + const prefix = backend ? 'iframe ' : ''; + return [ +{ + content: 'eLearning: add content to section', + trigger: `${prefix}div.o_wslides_slide_list_category_header:contains("${sectionName}") a:contains("Add Content")`, +}, { + content: 'eLearning: click on image', + trigger: `${prefix}a[data-slide-category=infographic]`, +}, { + content: 'eLearning: choose upload from device', + trigger: `${prefix}#source_type_local_file`, +}, { + content: 'eLearning: load image', + trigger: 'body', + run: async () => { + const uploadInput = backend ? + document.getElementsByTagName('iframe')[0].contentWindow.document.getElementById('upload') : + document.getElementById('upload'); + await fillInFileInput(uploadInput, 'Overview.png', 'image/png', testPngImage); + }, +}, { + content: 'eLearning: ensure that the preview is displayed which means that data is loaded and can be submitted', + trigger: `${prefix}#slide-image[src*="data:"]`, +}, { + content: 'eLearning: create and publish slide', + trigger: `${prefix}footer.modal-footer button:contains("Publish")`, +}, { + content: 'eLearning: launch content', + trigger: `${prefix}a.o_wslides_js_slides_list_slide_link:contains("Overview")`, +}, { + content: 'eLearning: check uploaded image presence and perform comparison', + trigger: (backend ? 'iframe ' : '') + '.o_wslides_fs_player img', + run: async () => { + const baseElement = backend ? document.querySelector('iframe').contentDocument : document; + const img = baseElement.querySelector('.o_wslides_fs_player img'); + if (await compareBase64Content(img.getAttribute('src'), 'Overview.png', 'image/png', testPngImage)) { + img.classList.add('o_wslides_tour_img_upload_success'); + } + } +}, { + content: 'eLearning: check uploaded image content', + trigger: `${prefix}.o_wslides_fs_player img.o_wslides_tour_img_upload_success`, +}, +{ + content: 'eLearning: back to course', + trigger: `${prefix}.o_wslides_fs_sidebar_header a:contains("Déboulonnate")`, +}]; +}; + +/** + * Test the upload of a pdf file as a new content and especially the binary content of the uploaded pdf. + */ +const addPdfToSection = function (sectionName, pageName, backend) { + const prefix = backend ? 'iframe ' : ''; + return [ +{ + content: 'eLearning: add content to section', + trigger: `${prefix}div.o_wslides_slide_list_category_header:contains("${sectionName}") a:contains("Add Content")`, +}, { + content: 'eLearning: click on document', + trigger: `${prefix}a[data-slide-category=document]`, +}, { + content: 'eLearning: choose upload from device', + trigger: `${prefix}#source_type_local_file`, +}, { + content: 'eLearning: load pdf', + trigger: 'body', + run: async () => { + const upload = backend ? + document.getElementsByTagName('iframe')[0].contentWindow.document.getElementById('upload') : + document.getElementById('upload'); + await fillInFileInput(upload, 'Exercise.pdf', 'application/pdf', testPdf); + }, +}, { + content: 'eLearning: ensure that the preview is displayed which means that data is loaded and can be submitted', + trigger: `${prefix}#slide-image[src*="data:"]`, +}, { + content: 'eLearning: create and publish slide', + trigger: `${prefix}footer.modal-footer button:contains("Publish")`, +}, { + content: 'eLearning: launch content', + trigger: `${prefix}a.o_wslides_js_slides_list_slide_link:contains("Exercise")`, +}, { + content: 'eLearning: check uploaded pdf presence and perform comparison', + trigger: (backend ? 'iframe ' : '') + '.o_wslides_fs_content', + run: async () => { + const baseElement = backend ? document.querySelector('iframe').contentDocument : document; + const pdfViewer = baseElement.querySelector('iframe.o_wslides_iframe_viewer').contentDocument + .querySelector('#PDFSlideViewer'); + if (await compareBase64Content(pdfViewer.getAttribute('data-slideurl'), 'Exercise.pdf', 'application/pdf', testPdf)) { + baseElement.querySelector('.o_wslides_fs_content').classList.add('o_wslides_tour_pdf_upload_success'); + } + }, +}, { + content: 'eLearning: check uploaded pdf content', + trigger: `${prefix}.o_wslides_fs_content.o_wslides_tour_pdf_upload_success`, +}, { + content: 'eLearning: back to course', + trigger: `${prefix}.o_wslides_fs_sidebar_header a:contains("Déboulonnate")`, +}]; +}; + var addExistingCourseTag = function (backend = false) { return [ { @@ -146,6 +276,8 @@ var addNewCourseTag = function (courseTagName, backend) { export default { addSection: addSection, + addImageToSection: addImageToSection, + addPdfToSection: addPdfToSection, addVideoToSection: addVideoToSection, addArticleToSection: addArticleToSection, addExistingCourseTag: addExistingCourseTag,