[REF] web, mail, *: tests: remove legacy file utils
* = mrp closes odoo/odoo#136141 Related: odoo/enterprise#47716 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -152,7 +152,9 @@ For more specific needs, you may also assign custom-defined actions
|
||||
# scss not needed in tests and depends on scss variables that are not in this bundle
|
||||
('remove', 'web_tour/static/src/tour_pointer/**/*.scss'),
|
||||
'web_tour/static/src/tour_service/**/*',
|
||||
'web/static/tests/legacy/helpers/test_utils_file.js',
|
||||
'web/static/tests/helpers/cleanup.js',
|
||||
'web/static/tests/helpers/utils.js',
|
||||
'web/static/tests/utils.js',
|
||||
'mail/static/tests/tours/discuss_public_tour.js',
|
||||
'mail/static/tests/tours/discuss_channel_as_guest_tour.js',
|
||||
],
|
||||
|
||||
@@ -4,11 +4,7 @@ import { startServer } from "@bus/../tests/helpers/mock_python_environment";
|
||||
|
||||
import { start } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
import { editInput } from "@web/../tests/helpers/utils";
|
||||
import { file } from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { click, contains } from "@web/../tests/utils";
|
||||
|
||||
const { createFile } = file;
|
||||
import { click, contains, createFile, inputFiles } from "@web/../tests/utils";
|
||||
|
||||
QUnit.module("file upload");
|
||||
|
||||
@@ -29,23 +25,23 @@ QUnit.test("no conflicts between file uploads", async () => {
|
||||
views: [[false, "form"]],
|
||||
});
|
||||
await click("button", { text: "Send message" });
|
||||
const file1 = await createFile({
|
||||
name: "text1.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
});
|
||||
await contains(".o-mail-Chatter .o-mail-Composer input[type=file]");
|
||||
editInput(document.body, ".o-mail-Chatter .o-mail-Composer input[type=file]", file1);
|
||||
await inputFiles(".o-mail-Chatter .o-mail-Composer input[type=file]", [
|
||||
await createFile({
|
||||
name: "text1.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
}),
|
||||
]);
|
||||
// Uploading file in the second thread: discuss.channel in chatWindow.
|
||||
await click("i[aria-label='Messages']");
|
||||
await click(".o-mail-NotificationItem");
|
||||
const file2 = await createFile({
|
||||
name: "text2.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
});
|
||||
await contains(".o-mail-ChatWindow .o-mail-Composer input[type=file]");
|
||||
editInput(document.body, ".o-mail-ChatWindow input[type=file]", file2);
|
||||
await inputFiles(".o-mail-ChatWindow .o-mail-Composer input[type=file]", [
|
||||
await createFile({
|
||||
name: "text2.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
}),
|
||||
]);
|
||||
await contains(".o-mail-Chatter .o-mail-AttachmentCard");
|
||||
await contains(".o-mail-ChatWindow .o-mail-AttachmentCard");
|
||||
});
|
||||
@@ -62,11 +58,12 @@ QUnit.test("Attachment shows spinner during upload", async () => {
|
||||
},
|
||||
});
|
||||
await openDiscuss(channelId);
|
||||
const file = await createFile({
|
||||
name: "text2.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
});
|
||||
editInput(document.body, ".o-mail-Composer input[type=file]", file);
|
||||
await inputFiles(".o-mail-Composer input[type=file]", [
|
||||
await createFile({
|
||||
name: "text2.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
}),
|
||||
]);
|
||||
await contains(".o-mail-AttachmentCard .fa-spinner");
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { createFile, inputFiles } from "@web/../tests/legacy/helpers/test_utils_file";
|
||||
import { createFile, inputFiles } from "@web/../tests/utils";
|
||||
|
||||
registry.category("web_tour.tours").add("mail/static/tests/tours/discuss_public_tour.js", {
|
||||
test: true,
|
||||
@@ -43,13 +43,12 @@ registry.category("web_tour.tours").add("mail/static/tests/tours/discuss_public_
|
||||
content: "Add one file in composer",
|
||||
trigger: ".o-mail-Composer button[aria-label='Attach files']",
|
||||
async run() {
|
||||
const file = await createFile({
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
name: "text.txt",
|
||||
});
|
||||
inputFiles(document.querySelector(".o-mail-Composer-coreMain .o_input_file"), [
|
||||
file,
|
||||
await inputFiles(".o-mail-Composer-coreMain .o_input_file", [
|
||||
await createFile({
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
name: "text.txt",
|
||||
}),
|
||||
]);
|
||||
},
|
||||
},
|
||||
@@ -90,17 +89,13 @@ registry.category("web_tour.tours").add("mail/static/tests/tours/discuss_public_
|
||||
content: "Add one more file in composer",
|
||||
trigger: ".o-mail-Message .o-mail-Composer button[aria-label='Attach files']",
|
||||
async run() {
|
||||
const file = await createFile({
|
||||
content: "hello 2",
|
||||
contentType: "text/plain",
|
||||
name: "extra.txt",
|
||||
});
|
||||
inputFiles(
|
||||
document.querySelector(
|
||||
".o-mail-Message .o-mail-Composer-coreMain .o_input_file"
|
||||
),
|
||||
[file]
|
||||
);
|
||||
inputFiles(".o-mail-Message .o-mail-Composer-coreMain .o_input_file", [
|
||||
await createFile({
|
||||
content: "hello 2",
|
||||
contentType: "text/plain",
|
||||
name: "extra.txt",
|
||||
}),
|
||||
]);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { createFile, inputFiles } from "@web/../tests/legacy/helpers/test_utils_file";
|
||||
import { createFile, inputFiles } from "@web/../tests/utils";
|
||||
|
||||
/**
|
||||
* This tour depends on data created by python test in charge of launching it.
|
||||
@@ -25,13 +25,12 @@ registry.category("web_tour.tours").add("mail/static/tests/tours/mail_full_compo
|
||||
content: "Add one file in composer",
|
||||
trigger: ".o-mail-Composer button[aria-label='Attach files']",
|
||||
async run() {
|
||||
const file = await createFile({
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
name: "text.txt",
|
||||
});
|
||||
inputFiles(document.querySelector(".o-mail-Composer-coreMain .o_input_file"), [
|
||||
file,
|
||||
await inputFiles(".o-mail-Composer-coreMain .o_input_file", [
|
||||
await createFile({
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
name: "text.txt",
|
||||
}),
|
||||
]);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,18 +7,16 @@ import { patchUiSize, SIZES } from "@mail/../tests/helpers/patch_ui_size";
|
||||
import { start } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
import { triggerHotkey } from "@web/../tests/helpers/utils";
|
||||
import { file } from "@web/../tests/legacy/helpers/test_utils";
|
||||
import {
|
||||
click,
|
||||
contains,
|
||||
createFile,
|
||||
dragenterFiles,
|
||||
dropFiles,
|
||||
insertText,
|
||||
scroll,
|
||||
} from "@web/../tests/utils";
|
||||
|
||||
const { createFile } = file;
|
||||
|
||||
QUnit.module("chatter");
|
||||
|
||||
QUnit.test("simple chatter on a record", async (assert) => {
|
||||
|
||||
@@ -7,9 +7,8 @@ import { start } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
import { fileUploadService } from "@web/core/file_upload/file_upload_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import testUtils from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { getFixture, nextTick } from "@web/../tests/helpers/utils";
|
||||
import { click, contains } from "@web/../tests/utils";
|
||||
import { click, contains, createFile, inputFiles } from "@web/../tests/utils";
|
||||
import { setupViewRegistries } from "@web/../tests/views/helpers";
|
||||
|
||||
addModelNamesToFetch(["mrp.document"]);
|
||||
@@ -85,24 +84,6 @@ QUnit.module('MrpDocumentsKanbanView', {
|
||||
});
|
||||
|
||||
QUnit.test('mrp: upload multiple files', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const file1 = await testUtils.file.createFile({
|
||||
name: 'text1.txt',
|
||||
content: 'hello, world',
|
||||
contentType: 'text/plain',
|
||||
});
|
||||
const file2 = await testUtils.file.createFile({
|
||||
name: 'text2.txt',
|
||||
content: 'hello, world',
|
||||
contentType: 'text/plain',
|
||||
});
|
||||
const file3 = await testUtils.file.createFile({
|
||||
name: 'text3.txt',
|
||||
content: 'hello, world',
|
||||
contentType: 'text/plain',
|
||||
});
|
||||
|
||||
const mockedXHRs = [];
|
||||
this.patchDocumentXHR(mockedXHRs, data => assert.step('xhrSend'));
|
||||
|
||||
@@ -122,31 +103,30 @@ QUnit.module('MrpDocumentsKanbanView', {
|
||||
views: [[false, 'kanban']],
|
||||
});
|
||||
|
||||
const fileInput = target.querySelector(".o_input_file");
|
||||
|
||||
let dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(file1);
|
||||
fileInput.files = dataTransfer.files;
|
||||
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
assert.verifySteps(['xhrSend']);
|
||||
|
||||
dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(file2);
|
||||
dataTransfer.items.add(file3);
|
||||
fileInput.files = dataTransfer.files;
|
||||
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
assert.verifySteps(['xhrSend']);
|
||||
await inputFiles(".o_control_panel_collapsed_create .o_input_file", [
|
||||
await createFile({
|
||||
name: "text1.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
}),
|
||||
]);
|
||||
assert.verifySteps(["xhrSend"]);
|
||||
await inputFiles(".o_control_panel_collapsed_create .o_input_file", [
|
||||
await createFile({
|
||||
name: "text2.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
}),
|
||||
await createFile({
|
||||
name: "text3.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
}),
|
||||
]);
|
||||
assert.verifySteps(["xhrSend"]);
|
||||
});
|
||||
|
||||
QUnit.test('mrp: upload progress bars', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const file1 = await testUtils.file.createFile({
|
||||
name: 'text1.txt',
|
||||
content: 'hello, world',
|
||||
contentType: 'text/plain',
|
||||
});
|
||||
|
||||
const mockedXHRs = [];
|
||||
this.patchDocumentXHR(mockedXHRs, data => assert.step('xhrSend'));
|
||||
|
||||
@@ -166,13 +146,14 @@ QUnit.module('MrpDocumentsKanbanView', {
|
||||
views: [[false, 'kanban']],
|
||||
});
|
||||
|
||||
const fileInput = target.querySelector(".o_input_file");
|
||||
|
||||
let dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(file1);
|
||||
fileInput.files = dataTransfer.files;
|
||||
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
assert.verifySteps(['xhrSend']);
|
||||
await inputFiles(".o_control_panel_collapsed_create .o_input_file", [
|
||||
await createFile({
|
||||
name: "text1.txt",
|
||||
content: "hello, world",
|
||||
contentType: "text/plain",
|
||||
}),
|
||||
]);
|
||||
assert.verifySteps(["xhrSend"]);
|
||||
|
||||
const progressEvent = new Event('progress', { bubbles: true });
|
||||
progressEvent.loaded = 250000000;
|
||||
|
||||
@@ -428,7 +428,9 @@ This module provides the core of the Odoo Web Client.
|
||||
'web.assets_tests': [
|
||||
# No tours are defined in web, but the bundle "assets_tests" is
|
||||
# first called in web.
|
||||
'web/static/tests/legacy/helpers/test_utils_file.js'
|
||||
'web/static/tests/helpers/cleanup.js',
|
||||
'web/static/tests/helpers/utils.js',
|
||||
'web/static/tests/utils.js',
|
||||
],
|
||||
'web.__assets_tests_call__': [
|
||||
'web/static/tests/ignore_missing_deps_start.js',
|
||||
|
||||
@@ -19,93 +19,97 @@ export function registerCleanup(callback) {
|
||||
cleanups.push(callback);
|
||||
}
|
||||
|
||||
QUnit.on("OdooAfterTestHook", (info) => {
|
||||
if (QUnit.config.debug) {
|
||||
return;
|
||||
}
|
||||
let cleanup;
|
||||
// note that this calls the cleanup callbacks in reverse order!
|
||||
while ((cleanup = cleanups.pop())) {
|
||||
try {
|
||||
cleanup(info);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (window.QUnit) {
|
||||
QUnit.on("OdooAfterTestHook", (info) => {
|
||||
if (QUnit.config.debug) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Check leftovers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* List of elements tolerated in the body after a test. The property "keep"
|
||||
* prevents the element from being removed (typically: qunit suite elements).
|
||||
*/
|
||||
const validElements = [
|
||||
// always in the body:
|
||||
{ tagName: "DIV", attr: "id", value: "qunit", keep: true },
|
||||
{ tagName: "DIV", attr: "id", value: "qunit-fixture", keep: true },
|
||||
// shouldn't be in the body after a test but are tolerated:
|
||||
{ tagName: "SCRIPT", attr: "id", value: "" },
|
||||
{ tagName: "DIV", attr: "class", value: "o_notification_manager" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip fade bs-tooltip-auto" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip fade bs-tooltip-auto show" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip tooltip-field-info fade bs-tooltip-auto" },
|
||||
{
|
||||
tagName: "DIV",
|
||||
attr: "class",
|
||||
value: "tooltip tooltip-field-info fade bs-tooltip-auto show",
|
||||
},
|
||||
{ tagName: "SPAN", attr: "class", value: "select2-hidden-accessible" },
|
||||
|
||||
// Due to a Document Kanban bug (already present in 12.0)
|
||||
{ tagName: "DIV", attr: "class", value: "ui-helper-hidden-accessible" },
|
||||
{
|
||||
tagName: "UL",
|
||||
attr: "class",
|
||||
value: "ui-menu ui-widget ui-widget-content ui-autocomplete ui-front",
|
||||
},
|
||||
{
|
||||
tagName: "UL",
|
||||
attr: "class",
|
||||
value: "ui-menu ui-widget ui-widget-content ui-autocomplete dropdown-menu ui-front", // many2ones
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* After each test, we check that there is no leftover in the DOM.
|
||||
*
|
||||
* Note: this event is not QUnit standard, we added it for this specific use case.
|
||||
* As a payload, an object with keys 'moduleName' and 'testName' is provided. It
|
||||
* is used to indicate the test that left elements in the DOM, when it happens.
|
||||
*/
|
||||
QUnit.on("OdooAfterTestHook", function (info) {
|
||||
if (QUnit.config.debug) {
|
||||
return;
|
||||
}
|
||||
const failed = info.testReport.getStatus() === "failed";
|
||||
const toRemove = [];
|
||||
// check for leftover elements in the body
|
||||
for (const bodyChild of document.body.children) {
|
||||
const tolerated = validElements.find(
|
||||
(e) => e.tagName === bodyChild.tagName && bodyChild.getAttribute(e.attr) === e.value
|
||||
);
|
||||
if (!failed && !tolerated) {
|
||||
QUnit.pushFailure(`Body still contains undesirable elements:\n${bodyChild.outerHTML}`);
|
||||
let cleanup;
|
||||
// note that this calls the cleanup callbacks in reverse order!
|
||||
while ((cleanup = cleanups.pop())) {
|
||||
try {
|
||||
cleanup(info);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
if (!tolerated || !tolerated.keep) {
|
||||
toRemove.push(bodyChild);
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Check leftovers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* List of elements tolerated in the body after a test. The property "keep"
|
||||
* prevents the element from being removed (typically: qunit suite elements).
|
||||
*/
|
||||
const validElements = [
|
||||
// always in the body:
|
||||
{ tagName: "DIV", attr: "id", value: "qunit", keep: true },
|
||||
{ tagName: "DIV", attr: "id", value: "qunit-fixture", keep: true },
|
||||
// shouldn't be in the body after a test but are tolerated:
|
||||
{ tagName: "SCRIPT", attr: "id", value: "" },
|
||||
{ tagName: "DIV", attr: "class", value: "o_notification_manager" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip fade bs-tooltip-auto" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip fade bs-tooltip-auto show" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip tooltip-field-info fade bs-tooltip-auto" },
|
||||
{
|
||||
tagName: "DIV",
|
||||
attr: "class",
|
||||
value: "tooltip tooltip-field-info fade bs-tooltip-auto show",
|
||||
},
|
||||
{ tagName: "SPAN", attr: "class", value: "select2-hidden-accessible" },
|
||||
|
||||
// Due to a Document Kanban bug (already present in 12.0)
|
||||
{ tagName: "DIV", attr: "class", value: "ui-helper-hidden-accessible" },
|
||||
{
|
||||
tagName: "UL",
|
||||
attr: "class",
|
||||
value: "ui-menu ui-widget ui-widget-content ui-autocomplete ui-front",
|
||||
},
|
||||
{
|
||||
tagName: "UL",
|
||||
attr: "class",
|
||||
value: "ui-menu ui-widget ui-widget-content ui-autocomplete dropdown-menu ui-front", // many2ones
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* After each test, we check that there is no leftover in the DOM.
|
||||
*
|
||||
* Note: this event is not QUnit standard, we added it for this specific use case.
|
||||
* As a payload, an object with keys 'moduleName' and 'testName' is provided. It
|
||||
* is used to indicate the test that left elements in the DOM, when it happens.
|
||||
*/
|
||||
QUnit.on("OdooAfterTestHook", function (info) {
|
||||
if (QUnit.config.debug) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// cleanup leftovers in #qunit-fixture
|
||||
const qunitFixture = document.getElementById("qunit-fixture");
|
||||
if (qunitFixture.children.length) {
|
||||
toRemove.push(...qunitFixture.children);
|
||||
}
|
||||
// remove unwanted elements if not in debug
|
||||
for (const el of toRemove) {
|
||||
el.remove();
|
||||
}
|
||||
document.body.classList.remove("modal-open");
|
||||
});
|
||||
const failed = info.testReport.getStatus() === "failed";
|
||||
const toRemove = [];
|
||||
// check for leftover elements in the body
|
||||
for (const bodyChild of document.body.children) {
|
||||
const tolerated = validElements.find(
|
||||
(e) => e.tagName === bodyChild.tagName && bodyChild.getAttribute(e.attr) === e.value
|
||||
);
|
||||
if (!failed && !tolerated) {
|
||||
QUnit.pushFailure(
|
||||
`Body still contains undesirable elements:\n${bodyChild.outerHTML}`
|
||||
);
|
||||
}
|
||||
if (!tolerated || !tolerated.keep) {
|
||||
toRemove.push(bodyChild);
|
||||
}
|
||||
}
|
||||
// cleanup leftovers in #qunit-fixture
|
||||
const qunitFixture = document.getElementById("qunit-fixture");
|
||||
if (qunitFixture.children.length) {
|
||||
toRemove.push(...qunitFixture.children);
|
||||
}
|
||||
// remove unwanted elements if not in debug
|
||||
for (const el of toRemove) {
|
||||
el.remove();
|
||||
}
|
||||
document.body.classList.remove("modal-open");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -146,6 +146,9 @@ export function patchWithCleanup(obj, patchValue) {
|
||||
* @returns {Element}
|
||||
*/
|
||||
export function getFixture() {
|
||||
if (!window.QUnit) {
|
||||
return document;
|
||||
}
|
||||
if (QUnit.config.debug) {
|
||||
return document.body;
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
import testUtilsDom from "@web/../tests/legacy/helpers/test_utils_dom";
|
||||
import testUtilsFields from "@web/../tests/legacy/helpers/test_utils_fields";
|
||||
import testUtilsFile from "@web/../tests/legacy/helpers/test_utils_file";
|
||||
import testUtilsMock from "@web/../tests/legacy/helpers/test_utils_mock";
|
||||
|
||||
function deprecated(fn, type) {
|
||||
@@ -107,19 +106,10 @@
|
||||
triggerKeydown: testUtilsFields.triggerKeydown,
|
||||
};
|
||||
|
||||
export const file = {
|
||||
createFile: testUtilsFile.createFile,
|
||||
dragoverFile: testUtilsFile.dragoverFile,
|
||||
dropFile: testUtilsFile.dropFile,
|
||||
dropFiles: testUtilsFile.dropFiles,
|
||||
inputFiles: testUtilsFile.inputFiles,
|
||||
};
|
||||
|
||||
export default {
|
||||
mock,
|
||||
dom,
|
||||
fields,
|
||||
file,
|
||||
|
||||
makeTestPromise: makeTestPromise,
|
||||
makeTestPromiseWithAssert: makeTestPromiseWithAssert,
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
/**
|
||||
* FILE Test Utils
|
||||
*
|
||||
* This module defines various utility functions to help simulate events with
|
||||
* files, such as drag-and-drop.
|
||||
*
|
||||
* Note that all methods defined in this module are exported in the main
|
||||
* testUtils file.
|
||||
*/
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a fake object 'dataTransfer', linked to some files, which is passed to
|
||||
* drag and drop events.
|
||||
*
|
||||
* @param {Object[]} files
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _createFakeDataTransfer(files) {
|
||||
return {
|
||||
dropEffect: 'all',
|
||||
effectAllowed: 'all',
|
||||
files,
|
||||
getData: function () {
|
||||
return files;
|
||||
},
|
||||
items: [],
|
||||
types: ['Files'],
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a file object, which can be used for drag-and-drop.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {string} data.name
|
||||
* @param {string} data.content
|
||||
* @param {string} data.contentType
|
||||
* @returns {Promise<Object>} resolved with file created
|
||||
*/
|
||||
export function createFile(data) {
|
||||
// Note: this is only supported by Chrome, and does not work in Incognito mode
|
||||
return new Promise(function (resolve, reject) {
|
||||
var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
|
||||
if (!requestFileSystem) {
|
||||
throw new Error('FileSystem API is not supported');
|
||||
}
|
||||
requestFileSystem(window.TEMPORARY, 1024 * 1024, function (fileSystem) {
|
||||
fileSystem.root.getFile(data.name, { create: true }, function (fileEntry) {
|
||||
fileEntry.createWriter(function (fileWriter) {
|
||||
fileWriter.onwriteend = function (e) {
|
||||
fileSystem.root.getFile(data.name, {}, function (fileEntry) {
|
||||
fileEntry.file(function (file) {
|
||||
resolve(file);
|
||||
});
|
||||
});
|
||||
};
|
||||
fileWriter.write(new Blob([ data.content ], { type: data.contentType }));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag a file over a DOM element
|
||||
*
|
||||
* @param {$.Element} $el
|
||||
* @param {Object} file must have been created beforehand (@see createFile)
|
||||
*/
|
||||
function dragoverFile($el, file) {
|
||||
var ev = new Event('dragover', { bubbles: true });
|
||||
Object.defineProperty(ev, 'dataTransfer', {
|
||||
value: _createFakeDataTransfer(file),
|
||||
});
|
||||
$el[0].dispatchEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a file on a DOM element.
|
||||
*
|
||||
* @param {$.Element} $el
|
||||
* @param {Object} file must have been created beforehand (@see createFile)
|
||||
*/
|
||||
function dropFile($el, file) {
|
||||
var ev = new Event('drop', { bubbles: true, });
|
||||
Object.defineProperty(ev, 'dataTransfer', {
|
||||
value: _createFakeDataTransfer([file]),
|
||||
});
|
||||
$el[0].dispatchEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop some files on a DOM element.
|
||||
*
|
||||
* @param {$.Element} $el
|
||||
* @param {Object[]} files must have been created beforehand (@see createFile)
|
||||
*/
|
||||
function dropFiles($el, files) {
|
||||
var ev = new Event('drop', { bubbles: true, });
|
||||
Object.defineProperty(ev, 'dataTransfer', {
|
||||
value: _createFakeDataTransfer(files),
|
||||
});
|
||||
$el[0].dispatchEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set files in a file input
|
||||
*
|
||||
* @param {DOM.Element} el
|
||||
* @param {Object[]} files must have been created beforehand
|
||||
* @see testUtils.file.createFile
|
||||
*/
|
||||
export function inputFiles(el, files) {
|
||||
// could not use _createFakeDataTransfer as el.files assignation will only
|
||||
// work with a real FileList object.
|
||||
const dataTransfer = new window.DataTransfer();
|
||||
for (const file of files) {
|
||||
dataTransfer.items.add(file);
|
||||
}
|
||||
el.files = dataTransfer.files;
|
||||
/**
|
||||
* Changing files programatically is not supposed to trigger the event but
|
||||
* it does in Chrome versions before 73 (which is on runbot), so in that
|
||||
* case there is no need to make a manual dispatch, because it would lead to
|
||||
* the files being added twice.
|
||||
*/
|
||||
const versionRaw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
|
||||
const chromeVersion = versionRaw ? parseInt(versionRaw[2], 10) : false;
|
||||
if (!chromeVersion || chromeVersion >= 73) {
|
||||
el.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Exposed API
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export default {
|
||||
createFile: createFile,
|
||||
dragoverFile: dragoverFile,
|
||||
dropFile: dropFile,
|
||||
dropFiles,
|
||||
inputFiles,
|
||||
};
|
||||
@@ -8,12 +8,39 @@ import {
|
||||
makeDeferred,
|
||||
triggerEvents as webTriggerEvents,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { file } from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { createFile as createFile2 } from "@web/../tests/legacy/helpers/test_utils_file";
|
||||
|
||||
const { inputFiles: webInputFiles } = file;
|
||||
|
||||
export const createFile = createFile2;
|
||||
/**
|
||||
* Create a file object, which can be used for drag-and-drop.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {string} data.name
|
||||
* @param {string} data.content
|
||||
* @param {string} data.contentType
|
||||
* @returns {Promise<Object>} resolved with file created
|
||||
*/
|
||||
export function createFile(data) {
|
||||
// Note: this is only supported by Chrome, and does not work in Incognito mode
|
||||
return new Promise(function (resolve, reject) {
|
||||
var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
|
||||
if (!requestFileSystem) {
|
||||
throw new Error("FileSystem API is not supported");
|
||||
}
|
||||
requestFileSystem(window.TEMPORARY, 1024 * 1024, function (fileSystem) {
|
||||
fileSystem.root.getFile(data.name, { create: true }, function (fileEntry) {
|
||||
fileEntry.createWriter(function (fileWriter) {
|
||||
fileWriter.onwriteend = function (e) {
|
||||
fileSystem.root.getFile(data.name, {}, function (fileEntry) {
|
||||
fileEntry.file(function (file) {
|
||||
resolve(file);
|
||||
});
|
||||
});
|
||||
};
|
||||
fileWriter.write(new Blob([data.content], { type: data.contentType }));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fake object 'dataTransfer', linked to some files,
|
||||
@@ -48,7 +75,7 @@ export async function click(selector, options = {}) {
|
||||
|
||||
/**
|
||||
* Waits until exactly one element matching the given `selector` is present in
|
||||
* `options.target` and then drags `files` on it.
|
||||
* `options.target` and then dragenters `files` on it.
|
||||
*
|
||||
* @param {string} selector
|
||||
* @param {Object[]} files
|
||||
@@ -58,6 +85,18 @@ export async function dragenterFiles(selector, files, options) {
|
||||
await contains(selector, { dragenterFiles: files, ...options });
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until exactly one element matching the given `selector` is present in
|
||||
* `options.target` and then dragovers `files` on it.
|
||||
*
|
||||
* @param {string} selector
|
||||
* @param {Object[]} files
|
||||
* @param {ContainsOptions} [options] forwarded to `contains`
|
||||
*/
|
||||
export async function dragoverFiles(selector, files, options) {
|
||||
await contains(selector, { dragoverFiles: files, ...options });
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until exactly one element matching the given `selector` is present in
|
||||
* `options.target` and then drops `files` on it.
|
||||
@@ -144,8 +183,22 @@ export async function triggerEvents(selector, events, options) {
|
||||
await contains(selector, { triggerEvents: events, ...options });
|
||||
}
|
||||
|
||||
function log(ok, message) {
|
||||
if (window.QUnit) {
|
||||
QUnit.assert.ok(ok, message);
|
||||
} else {
|
||||
if (ok) {
|
||||
console.log(message);
|
||||
} else {
|
||||
console.error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let hasUsedContainsPositively = false;
|
||||
QUnit.testStart(() => (hasUsedContainsPositively = false));
|
||||
if (window.QUnit) {
|
||||
QUnit.testStart(() => (hasUsedContainsPositively = false));
|
||||
}
|
||||
/**
|
||||
* @typedef {Object} ContainsOptions
|
||||
* @property {[string, ContainsOptions]} [after] if provided, the found element(s) must be after the
|
||||
@@ -161,7 +214,8 @@ QUnit.testStart(() => (hasUsedContainsPositively = false));
|
||||
* multiple rules that have to match at the same time.
|
||||
* @property {number} [count=1] numbers of elements to be found to declare the contains check
|
||||
* as successful. Elements are counted after applying all other filters.
|
||||
* @property {Object[]} [dragenterFiles] if provided, drags the given files on the found element
|
||||
* @property {Object[]} [dragenterFiles] if provided, dragenters the given files on the found element
|
||||
* @property {Object[]} [dragoverFiles] if provided, dragovers the given files on the found element
|
||||
* @property {Object[]} [dropFiles] if provided, drops the given files on the found element
|
||||
* @property {Object[]} [inputFiles] if provided, inputs the given files on the found element
|
||||
* @property {{content:string, replace:boolean}} [insertText] if provided, adds to (or replace) the
|
||||
@@ -322,16 +376,16 @@ class Contains {
|
||||
: `${message} Parent not found.`;
|
||||
if (this.parentContains) {
|
||||
if (this.parentContains.successMessage) {
|
||||
QUnit.assert.ok(true, this.parentContains.successMessage);
|
||||
log(true, this.parentContains.successMessage);
|
||||
} else {
|
||||
this.parentContains.executeError();
|
||||
}
|
||||
}
|
||||
QUnit.assert.ok(false, message);
|
||||
log(false, message);
|
||||
this.def?.reject(new Error(message));
|
||||
for (const childContains of this.childrenContains || []) {
|
||||
if (childContains.successMessage) {
|
||||
QUnit.assert.ok(true, childContains.successMessage);
|
||||
log(true, childContains.successMessage);
|
||||
} else {
|
||||
childContains.executeError();
|
||||
}
|
||||
@@ -360,13 +414,21 @@ class Contains {
|
||||
});
|
||||
}
|
||||
if (this.options.dragenterFiles) {
|
||||
message = `${message} and dragged ${this.options.dragenterFiles.length} file(s)`;
|
||||
message = `${message} and dragentered ${this.options.dragenterFiles.length} file(s)`;
|
||||
const ev = new Event("dragenter", { bubbles: true });
|
||||
Object.defineProperty(ev, "dataTransfer", {
|
||||
value: createFakeDataTransfer(this.options.dragenterFiles),
|
||||
});
|
||||
el.dispatchEvent(ev);
|
||||
}
|
||||
if (this.options.dragoverFiles) {
|
||||
message = `${message} and dragovered ${this.options.dragoverFiles.length} file(s)`;
|
||||
const ev = new Event("dragover", { bubbles: true });
|
||||
Object.defineProperty(ev, "dataTransfer", {
|
||||
value: createFakeDataTransfer(this.options.dragoverFiles),
|
||||
});
|
||||
el.dispatchEvent(ev);
|
||||
}
|
||||
if (this.options.dropFiles) {
|
||||
message = `${message} and dropped ${this.options.dropFiles.length} file(s)`;
|
||||
const ev = new Event("drop", { bubbles: true });
|
||||
@@ -377,7 +439,24 @@ class Contains {
|
||||
}
|
||||
if (this.options.inputFiles) {
|
||||
message = `${message} and inputted ${this.options.inputFiles.length} file(s)`;
|
||||
webInputFiles(el, this.options.inputFiles);
|
||||
// could not use _createFakeDataTransfer as el.files assignation will only
|
||||
// work with a real FileList object.
|
||||
const dataTransfer = new window.DataTransfer();
|
||||
for (const file of this.options.inputFiles) {
|
||||
dataTransfer.items.add(file);
|
||||
}
|
||||
el.files = dataTransfer.files;
|
||||
/**
|
||||
* Changing files programatically is not supposed to trigger the event but
|
||||
* it does in Chrome versions before 73 (which is on runbot), so in that
|
||||
* case there is no need to make a manual dispatch, because it would lead to
|
||||
* the files being added twice.
|
||||
*/
|
||||
const versionRaw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
|
||||
const chromeVersion = versionRaw ? parseInt(versionRaw[2], 10) : false;
|
||||
if (!chromeVersion || chromeVersion >= 73) {
|
||||
el.dispatchEvent(new Event("change"));
|
||||
}
|
||||
}
|
||||
if (this.options.insertText !== undefined) {
|
||||
message = `${message} and inserted text "${this.options.insertText.content}" (replace: ${this.options.insertText.replace})`;
|
||||
@@ -421,11 +500,11 @@ class Contains {
|
||||
});
|
||||
}
|
||||
if (this.parentContains) {
|
||||
QUnit.assert.ok(true, this.parentContains.successMessage);
|
||||
log(true, this.parentContains.successMessage);
|
||||
}
|
||||
QUnit.assert.ok(true, message);
|
||||
log(true, message);
|
||||
for (const childContains of this.childrenContains) {
|
||||
QUnit.assert.ok(true, childContains.successMessage);
|
||||
log(true, childContains.successMessage);
|
||||
}
|
||||
this.def?.resolve();
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ def wrap_with_qunit_module(url, content):
|
||||
"""
|
||||
Wraps the test file content (source code) with the QUnit.module('module_name', function() {...}).
|
||||
"""
|
||||
if "tests" in url and re.search(r'QUnit\.(test|debug|only)', content):
|
||||
if "tests" in url and re.search(r'QUnit\.(test|debug|only)\(', content):
|
||||
match = URL_RE.match(url)
|
||||
return f"""QUnit.module("{match["module"]}", function() {{{content}}});"""
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user