summaryrefslogtreecommitdiff
path: root/addons/web/static/tests/helpers/test_utils_file.js
blob: ecdd85e52d627c45df6b62cbf3e27003b208fa03 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
odoo.define('web.test_utils_file', function () {
"use strict";

/**
 * 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
 */
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
 */
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
//------------------------------------------------------------------------------

return {
    createFile: createFile,
    dragoverFile: dragoverFile,
    dropFile: dropFile,
    dropFiles,
    inputFiles,
};

});