diff --git a/addons/test_website/__manifest__.py b/addons/test_website/__manifest__.py
index 0bddc6f870d..2ad2c239bc1 100644
--- a/addons/test_website/__manifest__.py
+++ b/addons/test_website/__manifest__.py
@@ -43,9 +43,6 @@ models which only purpose is to run tests.""",
'web.assets_tests': [
'test_website/static/tests/tours/*',
],
- 'web.qunit_suite_tests': [
- 'test_website/static/tests/*.js',
- ],
},
'license': 'LGPL-3',
}
diff --git a/addons/test_website/static/tests/field_html_file_upload.js b/addons/test_website/static/tests/field_html_file_upload.js
deleted file mode 100644
index 99814dcdd57..00000000000
--- a/addons/test_website/static/tests/field_html_file_upload.js
+++ /dev/null
@@ -1,168 +0,0 @@
-/** @odoo-module **/
-
-import { FileSelectorControlPanel } from '@web_editor/components/media_dialog/file_selector';
-import { getFixture, patchWithCleanup } from "@web/../tests/helpers/utils";
-import { HtmlField } from '@web_editor/js/backend/html_field';
-import {registry} from '@web/core/registry';
-import testUtils from '@web/../tests/legacy/helpers/test_utils';
-import { uploadService } from '@web_editor/components/upload_progress_toast/upload_service';
-import { unsplashService } from '@web_unsplash/services/unsplash_service';
-import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
-import { click } from "@web/../tests/utils";
-import weTestUtils from '@web_editor/../tests/test_utils';
-import {Wysiwyg} from '@web_editor/js/wysiwyg/wysiwyg';
-import { useEffect } from "@odoo/owl";
-
-QUnit.module('field html file upload', {
- beforeEach: function () {
- this.data = weTestUtils.wysiwygData({
- 'mail.compose.message': {
- fields: {
- display_name: {
- string: "Displayed name",
- type: "char"
- },
- body: {
- string: "Message Body inline (to send)",
- type: "html"
- },
- attachment_ids: {
- string: "Attachments",
- type: "many2many",
- relation: "ir.attachment",
- }
- },
- records: [{
- id: 1,
- display_name: "Some Composer",
- body: "Hello",
- attachment_ids: [],
- }],
- },
- });
- },
-}, function () {
- QUnit.test('media dialog: upload', async function (assert) {
- assert.expect(8);
- const onAttachmentChangeTriggered = testUtils.makeTestPromise();
- patchWithCleanup(HtmlField.prototype, {
- _onAttachmentChange(event) {
- super._onAttachmentChange(event);
- onAttachmentChangeTriggered.resolve(true);
- }
- });
- const defFileSelector = testUtils.makeTestPromise();
- const onChangeTriggered = testUtils.makeTestPromise();
- const webSaveTriggered = testUtils.makeTestPromise();
- patchWithCleanup(FileSelectorControlPanel.prototype, {
- setup() {
- super.setup();
- useEffect(() => {
- defFileSelector.resolve(true);
- }, () => []);
- },
- async onChangeFileInput() {
- super.onChangeFileInput();
- onChangeTriggered.resolve(true);
- }
- });
- patchWithCleanup(Wysiwyg.prototype, {
- async _getColorpickerTemplate() {
- return weTestUtils.COLOR_PICKER_TEMPLATE;
- }
- });
-
- // create and load form view
- const serviceRegistry = registry.category("services");
- serviceRegistry.add("upload", uploadService);
- serviceRegistry.add("unsplash", unsplashService);
- const serverData = {
- models: this.data,
- };
- serverData.actions = {
- 1: {
- id: 1,
- name: "test",
- res_model: "mail.compose.message",
- type: "ir.actions.act_window",
- views: [[false, "form"]],
- },
- };
- serverData.views = {
- "mail.compose.message,false,search": "",
- "mail.compose.message,false,form": `
-
`,
- };
- const mockRPC = (route, args) => {
- if (args.method === "web_save") {
- const createVals = args.args[1];
- assert.ok(createVals && createVals.attachment_ids);
- assert.equal(createVals.attachment_ids[0][0], 4); // link command
- assert.equal(createVals.attachment_ids[0][1], 5); // on attachment id "5"
- webSaveTriggered.resolve();
- }
- if (route === "/web_editor/attachment/add_data") {
- const attachment = {
- id: 5,
- name: "test.jpg",
- description: false,
- mimetype: "image/jpeg",
- checksum: "7951a43bbfb08fd742224ada280913d1897b89ab",
- url: false,
- type: "binary",
- res_id: 0,
- res_model: "mail.compose.message",
- public: false,
- access_token: false,
- image_src: "/web/image/1-a0e63e61/test.jpg",
- image_width: 1,
- image_height: 1,
- original_id: false,
- };
- serverData.models['ir.attachment'].records.push({...attachment});
- return Promise.resolve(attachment);
- } else if (route === "/web/dataset/call_kw/ir.attachment/generate_access_token") {
- return Promise.resolve(["129a52e1-6bf2-470a-830e-8e368b022e13"]);
- }
- };
- const webClient = await createWebClient({ serverData, mockRPC });
- await doAction(webClient, 1);
- //trigger wysiwyg mediadialog
- const fixture = getFixture();
- const formField = fixture.querySelector('.o_field_html[name="body"]');
- const textInput = formField.querySelector('.note-editable p');
- textInput.innerText = "test";
- const pText = $(textInput).contents()[0];
- Wysiwyg.setRange(pText, 1, pText, 2);
- await new Promise((resolve) => setTimeout(resolve)); //ensure fully set up
- const wysiwyg = $(textInput.parentElement).data('wysiwyg');
- wysiwyg.openMediaDialog();
- assert.ok(await Promise.race([defFileSelector, new Promise((res, _) => setTimeout(() => res(false), 400))]), "File Selector did not mount");
- // upload test
- const fileInputs = document.querySelectorAll(".o_select_media_dialog input.d-none.o_file_input");
- const fileB64 = '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigD//2Q==';
- const fileBytes = new Uint8Array(atob(fileB64).split('').map(char => char.charCodeAt(0)));
- // redefine 'files' so we can put mock data in through js
- fileInputs.forEach((input) => Object.defineProperty(input, 'files', {
- value: [new File(fileBytes, "test.jpg", { type: 'image/jpeg' })],
- }));
- fileInputs.forEach(input => {
- input.dispatchEvent(new Event('change', {}));
- });
-
- assert.ok(await Promise.race([onChangeTriggered, new Promise((res, _) => setTimeout(() => res(false), 400))]),
- "File change event was not triggered");
- assert.ok(await Promise.race([onAttachmentChangeTriggered, new Promise((res, _) => setTimeout(() => res(false), 400))]),
- "_onAttachmentChange was not called with the new attachment, necessary for unsused upload cleanup on backend");
-
- // wait to check that dom is properly updated
- await new Promise((res, _) => setTimeout(() => res(false), 400));
- assert.ok(fixture.querySelector('.o_attachment[title="test.jpg"]'));
-
- await click(".o_form_button_save");
- await webSaveTriggered;
- });
-});
diff --git a/addons/web_editor/static/tests/html_field_tests.js b/addons/web_editor/static/tests/html_field_tests.js
index c4538e069ae..94b3a9e19ed 100644
--- a/addons/web_editor/static/tests/html_field_tests.js
+++ b/addons/web_editor/static/tests/html_field_tests.js
@@ -1,14 +1,18 @@
/** @odoo-module **/
import { click, editInput, getFixture, makeDeferred, mockSendBeacon, nextTick, patchWithCleanup } from "@web/../tests/helpers/utils";
+import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
+import { FileSelectorControlPanel } from "@web_editor/components/media_dialog/file_selector";
import { FormController } from '@web/views/form/form_controller';
import { HtmlField } from "@web_editor/js/backend/html_field";
import { MediaDialog } from "@web_editor/components/media_dialog/media_dialog";
import { parseHTML, setSelection } from "@web_editor/js/editor/odoo-editor/src/utils/utils";
-import { onRendered } from "@odoo/owl";
-import { wysiwygData } from "@web_editor/../tests/test_utils";
+import { onRendered, useEffect } from "@odoo/owl";
+import { registry } from "@web/core/registry";
+import { COLOR_PICKER_TEMPLATE, wysiwygData } from "@web_editor/../tests/test_utils";
import { OdooEditor } from '@web_editor/js/editor/odoo-editor/src/OdooEditor';
+import { uploadService } from "@web_editor/components/upload_progress_toast/upload_service";
import { Wysiwyg } from "@web_editor/js/wysiwyg/wysiwyg";
import { insertText } from '@web_editor/js/editor/odoo-editor/test/utils';
@@ -1056,3 +1060,194 @@ QUnit.module("WebEditor.HtmlField", ({ beforeEach }) => {
});
});
+
+export const mediaDialogServices = {
+ upload: uploadService,
+};
+
+export const uploadTestModule = QUnit.module(
+ "WebEditor.HtmlField.upload",
+ {
+ beforeEach: function () {
+ this.data = wysiwygData({
+ "mail.compose.message": {
+ fields: {
+ display_name: {
+ string: "Displayed name",
+ type: "char",
+ },
+ body: {
+ string: "Message Body inline (to send)",
+ type: "html",
+ },
+ attachment_ids: {
+ string: "Attachments",
+ type: "many2many",
+ relation: "ir.attachment",
+ },
+ },
+ records: [
+ {
+ id: 1,
+ display_name: "Some Composer",
+ body: "Hello",
+ attachment_ids: [],
+ },
+ ],
+ },
+ });
+ },
+ },
+ function () {
+ QUnit.test("media dialog: upload", async function (assert) {
+ assert.expect(7);
+ const onAttachmentChangeTriggered = makeDeferred();
+ patchWithCleanup(HtmlField.prototype, {
+ _onAttachmentChange(event) {
+ super._onAttachmentChange(event);
+ onAttachmentChangeTriggered.resolve(true);
+ },
+ });
+ const defFileSelector = makeDeferred();
+ const onChangeTriggered = makeDeferred();
+ const webSaveTriggered = makeDeferred();
+ patchWithCleanup(FileSelectorControlPanel.prototype, {
+ setup() {
+ super.setup();
+ useEffect(
+ () => {
+ defFileSelector.resolve(true);
+ },
+ () => [],
+ );
+ },
+ async onChangeFileInput() {
+ super.onChangeFileInput();
+ onChangeTriggered.resolve(true);
+ },
+ });
+ patchWithCleanup(Wysiwyg.prototype, {
+ async _getColorpickerTemplate() {
+ return COLOR_PICKER_TEMPLATE;
+ },
+ });
+ // create and load form view
+ const serviceRegistry = registry.category("services");
+ for (const [serviceName, serviceDefinition] of Object.entries(mediaDialogServices)) {
+ serviceRegistry.add(serviceName, serviceDefinition);
+ }
+ const serverData = {
+ models: this.data,
+ };
+ serverData.actions = {
+ 1: {
+ id: 1,
+ name: "test",
+ res_model: "mail.compose.message",
+ type: "ir.actions.act_window",
+ views: [[false, "form"]],
+ },
+ };
+ serverData.views = {
+ "mail.compose.message,false,search": "",
+ "mail.compose.message,false,form": `
+ `,
+ };
+ const mockRPC = (route, args) => {
+ if (args.method === "web_save") {
+ const createVals = args.args[1];
+ assert.ok(createVals && createVals.attachment_ids);
+ assert.equal(createVals.attachment_ids[0][0], 4); // link command
+ assert.equal(createVals.attachment_ids[0][1], 5); // on attachment id "5"
+ webSaveTriggered.resolve();
+ }
+ if (route === "/web_editor/attachment/add_data") {
+ const attachment = {
+ id: 5,
+ name: "test.jpg",
+ description: false,
+ mimetype: "image/jpeg",
+ checksum: "7951a43bbfb08fd742224ada280913d1897b89ab",
+ url: false,
+ type: "binary",
+ res_id: 0,
+ res_model: "mail.compose.message",
+ public: false,
+ access_token: false,
+ image_src: "/web/image/1-a0e63e61/test.jpg",
+ image_width: 1,
+ image_height: 1,
+ original_id: false,
+ };
+ serverData.models["ir.attachment"].records.push({ ...attachment });
+ return Promise.resolve(attachment);
+ } else if (route === "/web/dataset/call_kw/ir.attachment/generate_access_token") {
+ return Promise.resolve(["129a52e1-6bf2-470a-830e-8e368b022e13"]);
+ }
+ };
+ const webClient = await createWebClient({ serverData, mockRPC });
+ await doAction(webClient, 1);
+ //trigger wysiwyg mediadialog
+ const fixture = getFixture();
+ const formField = fixture.querySelector('.o_field_html[name="body"]');
+ const textInput = formField.querySelector(".note-editable p");
+ textInput.innerText = "test";
+ const pText = $(textInput).contents()[0];
+ Wysiwyg.setRange(pText, 1, pText, 2);
+ await new Promise((resolve) => setTimeout(resolve)); //ensure fully set up
+ const wysiwyg = $(textInput.parentElement).data("wysiwyg");
+ wysiwyg.openMediaDialog();
+ assert.ok(
+ await Promise.race([
+ defFileSelector,
+ new Promise((res, _) => setTimeout(() => res(false), 400)),
+ ]),
+ "File Selector did not mount",
+ );
+ // upload test
+ const fileInputs = document.querySelectorAll(
+ ".o_select_media_dialog input.d-none.o_file_input",
+ );
+ const fileB64 =
+ "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigD//2Q==";
+ const fileBytes = new Uint8Array(
+ atob(fileB64)
+ .split("")
+ .map((char) => char.charCodeAt(0)),
+ );
+ // redefine 'files' so we can put mock data in through js
+ fileInputs.forEach((input) =>
+ Object.defineProperty(input, "files", {
+ value: [new File(fileBytes, "test.jpg", { type: "image/jpeg" })],
+ }),
+ );
+ fileInputs.forEach((input) => {
+ input.dispatchEvent(new Event("change", {}));
+ });
+
+ assert.ok(
+ await Promise.race([
+ onChangeTriggered,
+ new Promise((res, _) => setTimeout(() => res(false), 400)),
+ ]),
+ "File change event was not triggered",
+ );
+ assert.ok(
+ await Promise.race([
+ onAttachmentChangeTriggered,
+ new Promise((res, _) => setTimeout(() => res(false), 400)),
+ ]),
+ "_onAttachmentChange was not called with the new attachment, necessary for unsused upload cleanup on backend"
+ );
+ // wait to check that dom is properly updated
+ await new Promise((res, _) => setTimeout(() => res(false), 400));
+ assert.ok(fixture.querySelector('.o_attachment[title="test.jpg"]'));
+
+ await click(fixture.querySelector(".o_form_button_save"));
+ await webSaveTriggered;
+ });
+ },
+);
diff --git a/addons/web_editor/static/tests/test_utils.js b/addons/web_editor/static/tests/test_utils.js
index 0646778a4f0..05557477afc 100644
--- a/addons/web_editor/static/tests/test_utils.js
+++ b/addons/web_editor/static/tests/test_utils.js
@@ -8,7 +8,7 @@ import { Wysiwyg } from '@web_editor/js/wysiwyg/wysiwyg';
import options from "@web_editor/js/editor/snippets.options";
import { TABLE_ATTRIBUTES, TABLE_STYLES } from '@web_editor/js/backend/convert_inline';
-const COLOR_PICKER_TEMPLATE = `
+export const COLOR_PICKER_TEMPLATE = `
diff --git a/addons/web_unsplash/__manifest__.py b/addons/web_unsplash/__manifest__.py
index e593cea6a4d..67c237dbe6f 100644
--- a/addons/web_unsplash/__manifest__.py
+++ b/addons/web_unsplash/__manifest__.py
@@ -20,6 +20,9 @@
'web_unsplash/static/src/components/media_dialog/*.xml',
'web_unsplash/static/src/services/unsplash_service.js',
],
+ 'web.qunit_suite_tests': [
+ 'web_unsplash/static/tests/**/*',
+ ],
},
'license': 'LGPL-3',
}
diff --git a/addons/web_unsplash/static/tests/html_field_tests.js b/addons/web_unsplash/static/tests/html_field_tests.js
new file mode 100644
index 00000000000..4cf80b0c7e8
--- /dev/null
+++ b/addons/web_unsplash/static/tests/html_field_tests.js
@@ -0,0 +1,7 @@
+/** @odoo-module **/
+
+import { unsplashService } from "@web_unsplash/services/unsplash_service";
+import { mediaDialogServices } from "@web_editor/../tests/html_field_tests";
+
+// update the list of required services to open the media dialog
+mediaDialogServices.unsplash = unsplashService;