[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:
Sébastien Theys
2023-09-22 11:55:58 +00:00
parent 929bf923c6
commit cc9e2c147c
13 changed files with 270 additions and 375 deletions
+3 -1
View File
@@ -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;
+3 -1
View File
@@ -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',
+91 -87
View File
@@ -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");
});
}
+3
View File
@@ -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,
};
+95 -16
View File
@@ -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();
}
+1 -1
View File
@@ -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: