import { expect, test } from "@odoo/hoot"; import { setInputFiles } from "@odoo/hoot-dom"; import { animationFrame } from "@odoo/hoot-mock"; import { clickSave, contains, defineModels, fields, mockService, models, mountView, onRpc, } from "@web/../tests/web_test_helpers"; class Turtle extends models.Model { picture_ids = fields.Many2many({ string: "Pictures", relation: "ir.attachment", }); _records = [{ id: 1, picture_ids: [17] }]; } class IrAttachment extends models.Model { _name = "ir.attachment"; name = fields.Char(); mimetype = fields.Char(); _records = [{ id: 17, name: "Marley&Me.jpg", mimetype: "jpg" }]; } defineModels([Turtle, IrAttachment]); test("widget many2many_binary", async () => { expect.assertions(17); mockService("http", () => ({ post(route, params) { expect(route).toBe("/web/binary/upload_attachment"); expect(params.ufile[0].name).toBe("fake_file.tiff", { message: "file is correctly uploaded to the server", }); const file = { id: 10, name: params.ufile[0].name, mimetype: "text/plain", }; IrAttachment._records.push(file); return JSON.stringify([file]); }, })); IrAttachment._views.list = ''; onRpc((args) => { if (args.method !== "get_views") { expect.step(args.route); } if (args.method === "web_read" && args.model === "turtle") { expect(args.kwargs.specification).toEqual({ display_name: {}, picture_ids: { fields: { mimetype: {}, name: {}, }, }, }); } if (args.method === "web_save" && args.model === "turtle") { expect(args.kwargs.specification).toEqual({ display_name: {}, picture_ids: { fields: { mimetype: {}, name: {}, }, }, }); } if (args.method === "web_read" && args.model === "ir.attachment") { expect(args.kwargs.specification).toEqual({ mimetype: {}, name: {}, }); } }); await mountView({ type: "form", resModel: "turtle", arch: `
`, resId: 1, }); expect("div.o_field_widget .oe_fileupload").toHaveCount(1); expect("div.o_field_widget .oe_fileupload .o_attachments").toHaveCount(1); expect("div.o_field_widget .oe_fileupload .o_attachment .o_attachment_delete").toHaveCount(1); expect("div.o_field_widget .oe_fileupload .o_attach").toHaveCount(1); expect("div.o_field_widget .oe_fileupload .o_attach").toHaveText("Pictures"); expect("input.o_input_file").toHaveAttribute("accept", "image/*"); expect.verifySteps(["/web/dataset/call_kw/turtle/web_read"]); // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.tiff", { type: "text/plain" }); await contains(".o_file_input_trigger").click(); await setInputFiles([file]); await animationFrame(); expect(".o_attachment:nth-child(2) .caption a:eq(0)").toHaveText("fake_file.tiff", { message: 'value of attachment should be "fake_file.tiff"', }); expect(".o_attachment:nth-child(2) .caption.small a").toHaveText("TIFF", { message: "file extension should be correct", }); expect(".o_attachment:nth-child(2) .o_image.o_hover").toHaveAttribute( "data-mimetype", "text/plain", { message: "preview displays the right mimetype" } ); // delete the attachment await contains("div.o_field_widget .oe_fileupload .o_attachment .o_attachment_delete").click(); await clickSave(); expect("div.o_field_widget .oe_fileupload .o_attachments").toHaveCount(1); expect.verifySteps([ "/web/dataset/call_kw/ir.attachment/web_read", "/web/dataset/call_kw/turtle/web_save", ]); }); test("widget many2many_binary displays notification on error", async () => { expect.assertions(12); mockService("http", () => ({ post(route, params) { expect(route).toBe("/web/binary/upload_attachment"); expect([params.ufile[0].name, params.ufile[1].name]).toEqual( ["good_file.txt", "bad_file.txt"], { message: "files are correctly sent to the server" } ); const files = [ { id: 10, name: params.ufile[0].name, mimetype: "text/plain", }, { id: 11, name: params.ufile[1].name, mimetype: "text/plain", error: `Error on file: ${params.ufile[1].name}`, }, ]; IrAttachment._records.push(files[0]); return JSON.stringify(files); }, })); IrAttachment._views.list = ''; await mountView({ type: "form", resModel: "turtle", arch: `
`, resId: 1, }); expect("div.o_field_widget .oe_fileupload").toHaveCount(1); expect("div.o_field_widget .oe_fileupload .o_attachments").toHaveCount(1); expect("div.o_field_widget .oe_fileupload .o_attach").toHaveCount(1); expect("div.o_field_widget .oe_fileupload .o_attachment .o_attachment_delete").toHaveCount(1); // Set and trigger the import of 2 files in the input const files = [ new File(["good_file"], "good_file.txt", { type: "text/plain" }), new File(["bad_file"], "bad_file.txt", { type: "text/plain" }), ]; await contains(".o_file_input_trigger").click(); await setInputFiles(files); await animationFrame(); expect(".o_attachment:nth-child(2) .caption a:eq(0)").toHaveText("good_file.txt", { message: 'value of attachment should be "good_file.txt"', }); expect("div.o_field_widget .oe_fileupload .o_attachments").toHaveCount(1); expect(".o_notification").toHaveCount(1); expect(".o_notification_title").toHaveText("Uploading error"); expect(".o_notification_content").toHaveText("Error on file: bad_file.txt"); expect(".o_notification_bar").toHaveClass("bg-danger"); });