import { defineDocumentSpreadsheetModels, getDocumentBasicData, } from "@documents_spreadsheet/../tests/helpers/data"; import { getEnrichedSearchArch } from "@documents/../tests/helpers/views/search"; import { mockActionService } from "@documents_spreadsheet/../tests/helpers/spreadsheet_test_utils"; import { describe, expect, test } from "@odoo/hoot"; import { click, dblclick, queryFirst, select, press } from "@odoo/hoot-dom"; import { animationFrame } from "@odoo/hoot-mock"; import { makeDocumentsSpreadsheetMockEnv } from "@documents_spreadsheet/../tests/helpers/model"; import { contains, mountView } from "@web/../tests/web_test_helpers"; describe.current.tags("desktop"); defineDocumentSpreadsheetModels(); const kanbanArch = /* xml */ `
`; const TEST_TEMPLATES = [ { id: 3, name: "Template 3", spreadsheet_data: "{}" }, { id: 4, name: "Template 4", spreadsheet_data: "{}" }, { id: 5, name: "Template 5", spreadsheet_data: "{}" }, { id: 6, name: "Template 6", spreadsheet_data: "{}" }, { id: 7, name: "Template 7", spreadsheet_data: "{}" }, { id: 8, name: "Template 8", spreadsheet_data: "{}" }, { id: 9, name: "Template 9", spreadsheet_data: "{}" }, { id: 10, name: "Template 10", spreadsheet_data: "{}" }, { id: 11, name: "Template 11", spreadsheet_data: "{}" }, { id: 12, name: "Template 12", spreadsheet_data: "{}" }, ]; /** * @typedef InitArgs * @property {Object} [serverData] * @property {Array} [additionalTemplates] * @property {Function} [mockRPC] */ /** * @param {InitArgs} args */ async function initTestEnvWithKanban(args = {}) { const data = args.serverData || getDocumentBasicData({}); data.models["spreadsheet.template"].records = data.models[ "spreadsheet.template" ].records.concat(args.additionalTemplates || []); await makeDocumentsSpreadsheetMockEnv({ ...args, serverData: data }); return mountView({ type: "kanban", resModel: "documents.document", arch: kanbanArch, searchViewArch: getEnrichedSearchArch(), }); } /** * @param {InitArgs} params */ async function initTestEnvWithBlankSpreadsheet(params = {}) { const serverData = getDocumentBasicData(); serverData.models["documents.document"] = { records: [ { name: "Folder 1", description: "Folder", type: "folder", id: 1, available_embedded_actions_ids: [], }, ], }; serverData.models["documents.document"] = { record: [ { name: "My spreadsheet", spreadsheet_data: "{}", is_favorited: false, folder_id: 1, handler: "spreadsheet", }, ], }; return initTestEnvWithKanban({ serverData, ...params }); } async function openTemplateDialog() { await contains(`.o_control_panel_main_buttons .dropdown-toggle`).click(); await contains(`.o-dropdown--menu .dropdown-item.o_documents_kanban_spreadsheet`).click(); } const dialogSelector = ".o-spreadsheet-templates-dialog"; test("Create spreadsheet from kanban view opens a modal", async function () { await initTestEnvWithKanban(); await openTemplateDialog(); expect(".o-spreadsheet-templates-dialog").toHaveCount(1, { message: "should have opened the template modal", }); expect(".o-spreadsheet-templates-dialog .modal-body .o_searchview").toHaveCount(1, { message: "The Modal should have a search view", }); }); test("Create spreadsheet from list view opens a modal", async function () { const serverData = getDocumentBasicData(); await makeDocumentsSpreadsheetMockEnv({ serverData }); await mountView({ resModel: "documents.document", type: "list", arch: ``, searchViewArch: getEnrichedSearchArch(), }); await openTemplateDialog(); expect(".o-spreadsheet-templates-dialog").toHaveCount(1, { message: "should have opened the template modal", }); expect(".o-spreadsheet-templates-dialog .modal-body .o_searchview").toHaveCount(1, { message: "The Modal should have a search view", }); }); test("Can search template in modal with searchbar", async function () { await initTestEnvWithKanban(); await openTemplateDialog(); expect(`${dialogSelector} .o-spreadsheet-grid:not(.o-spreadsheet-grid-ghost-item)`).toHaveCount( 3 ); expect(`${dialogSelector} .o-spreadsheet-grid:first`).toHaveText("Blank spreadsheet"); await contains(`${dialogSelector} .o_searchview_input`).edit("Template 1"); await press("Enter"); await animationFrame(); expect(`${dialogSelector} .o-spreadsheet-grid:not(.o-spreadsheet-grid-ghost-item)`).toHaveCount( 2 ); expect(`${dialogSelector} .o-spreadsheet-grid:first`).toHaveText("Blank spreadsheet"); }); test("Can fetch next templates", async function () { let fetch = 0; const mockRPC = async function (route, args) { if (args.method === "web_search_read" && args.model === "spreadsheet.template") { fetch++; expect(args.kwargs.limit).toBe(9); expect.step("fetch_templates"); if (fetch === 1) { expect(args.kwargs.offset).toBe(0); } else if (fetch === 2) { expect(args.kwargs.offset).toBe(9); } } if (args.method === "search_read" && args.model === "ir.model") { return [{ name: "partner" }]; } }; await initTestEnvWithKanban({ additionalTemplates: TEST_TEMPLATES, mockRPC }); await openTemplateDialog(); expect(`${dialogSelector} .o-spreadsheet-grid:not(.o-spreadsheet-grid-ghost-item)`).toHaveCount( 10 ); await contains(`${dialogSelector} .o_pager_next`).click(); expect.verifySteps(["fetch_templates", "fetch_templates"]); }); test("Can create a blank spreadsheet without access to the template model", async function () { const mockRPC = async function (route, args) { if (args.model === "spreadsheet.template" && args.method === "has_access") { return false; } }; await initTestEnvWithKanban({ additionalTemplates: TEST_TEMPLATES, mockRPC }); await openTemplateDialog(); expect(`${dialogSelector} .o-spreadsheet-grid:not(.o-spreadsheet-grid-ghost-item)`).toHaveCount( 1 ); }); test("Disable create button if no template is selected", async function () { await initTestEnvWithKanban({ additionalTemplates: TEST_TEMPLATES }); await openTemplateDialog(); // select template await click(`${dialogSelector} .o-spreadsheet-grid-image:eq(1)`); // change page; no template should be selected await contains(`${dialogSelector} .o_pager_next`).click(); expect(".o-spreadsheet-grid-selected").toHaveCount(0); expect(`${dialogSelector} .o-spreadsheet-create`).toHaveAttribute("disabled"); }); test("Can create a blank spreadsheet from template dialog", async function () { const mockDoAction = (action) => { expect.step("redirect"); expect(action.tag).toBe("action_open_spreadsheet"); }; await initTestEnvWithBlankSpreadsheet({ mockRPC: async function (route, args) { if ( args.model === "documents.document" && args.method === "action_open_new_spreadsheet" ) { expect(args.args[0].folder_id).toBe(false); expect.step("action_open_new_spreadsheet"); } }, }); mockActionService(mockDoAction); // ### With confirm button await openTemplateDialog(); // select blank spreadsheet await click(`${dialogSelector} .o-spreadsheet-grid-image`); await contains(`${dialogSelector} .o-spreadsheet-create`).click(); expect.verifySteps(["action_open_new_spreadsheet", "redirect"]); // ### With double click on image await openTemplateDialog(); await click(`${dialogSelector} .o-spreadsheet-grid-image`); await dblclick(`${dialogSelector} .o-spreadsheet-grid-image`); await animationFrame(); expect.verifySteps(["action_open_new_spreadsheet", "redirect"]); }); test("Context is transmitted when creating spreadsheet", async function () { const serverData = await getDocumentBasicData({ "documents.document,false,kanban": ` `, "documents.document,false,search": getEnrichedSearchArch(), }); await makeDocumentsSpreadsheetMockEnv({ mockRPC: async function (route, args) { if (args.method === "action_open_new_spreadsheet") { expect.step("action_open_new_spreadsheet"); expect(args.kwargs.context.default_res_id).toBe(42); expect(args.kwargs.context.default_res_model).toBe("test.model"); } }, serverData, }); await mountView({ context: { default_res_model: "test.model", default_res_id: 42, }, resModel: "documents.document", type: "kanban", searchViewArch: getEnrichedSearchArch(), arch: kanbanArch, }); await openTemplateDialog(); // select blank spreadsheet await click(`${dialogSelector} .o-spreadsheet-grid-image`); await contains(`${dialogSelector} .o-spreadsheet-create`).click(); expect.verifySteps(["action_open_new_spreadsheet"]); }); test("Can create a spreadsheet from a template", async function () { const mockDoAction = (action) => { expect.step("redirect"); expect(action.tag).toBe("an_action"); }; await initTestEnvWithKanban({ additionalTemplates: TEST_TEMPLATES, mockRPC: async function (route, args) { if ( args.model === "spreadsheet.template" && args.method === "action_create_spreadsheet" ) { expect.step("action_create_spreadsheet"); expect(args.args[1].folder_id).toBe(false); const action = { type: "ir.actions.client", tag: "an_action", }; return action; } }, }); mockActionService(mockDoAction); // ### With confirm button await openTemplateDialog(); await click(`${dialogSelector} .o-spreadsheet-grid-image:eq(1)`); await contains(`${dialogSelector} .o-spreadsheet-create`).click(); expect.verifySteps(["action_create_spreadsheet", "redirect"]); // ### With double click on image await openTemplateDialog(); await click(`${dialogSelector} .o-spreadsheet-grid-image:eq(1)`); await dblclick(`${dialogSelector} .o-spreadsheet-grid-image:eq(1)`); await animationFrame(); expect.verifySteps(["action_create_spreadsheet", "redirect"]); }); test("The folder selection should not display Trash folder", async function () { await initTestEnvWithKanban(); await openTemplateDialog(); expect( ".o-spreadsheet-templates-dialog .o-spreadsheet-grid-item-name:contains(TRASH)" ).toHaveCount(0, { message: "Trash folder should not be present in the selection", }); }); test("Offset reset to zero after searching for template in template dialog", async function () { const mockRPC = async function (route, args) { if (args.method === "web_search_read" && args.model === "spreadsheet.template") { expect.step( JSON.stringify({ offset: args.kwargs.offset, limit: args.kwargs.limit, }) ); } }; await initTestEnvWithKanban({ additionalTemplates: TEST_TEMPLATES, mockRPC }); await openTemplateDialog(); expect(`${dialogSelector} .o-spreadsheet-grid:not(.o-spreadsheet-grid-ghost-item)`).toHaveCount( 10 ); await contains(`${dialogSelector} .o_pager_next`).click(); expect.verifySteps([ JSON.stringify({ offset: 0, limit: 9 }), JSON.stringify({ offset: 9, limit: 9 }), ]); await contains(`${dialogSelector} .o_searchview_input`).edit("Template 1"); await press("Enter"); await animationFrame(); await animationFrame(); expect(`${dialogSelector} .o-spreadsheet-grid:not(.o-spreadsheet-grid-ghost-item)`).toHaveCount( 5 ); // Blank template, Template 1, Template 10, Template 11, Template 12 expect.verifySteps([JSON.stringify({ offset: 0, limit: 9 })]); expect(`${dialogSelector} .o_pager_value `).toHaveText("1-4", { message: "Pager should be reset to 1-4 after searching for a template", }); }); test("Can navigate through templates with keyboard", async function () { await initTestEnvWithKanban({ additionalTemplates: TEST_TEMPLATES }); await openTemplateDialog(); const defaultTemplate = queryFirst( `${dialogSelector} .o-spreadsheet-grid.o-blank-spreadsheet-grid .o-spreadsheet-grid-image` ); expect(defaultTemplate).toHaveClass("o-spreadsheet-grid-selected"); // Navigate to the next template await contains(defaultTemplate).press("ArrowRight"); expect(defaultTemplate).not.toHaveClass("o-spreadsheet-grid-selected"); const firstTemplate = queryFirst(`${dialogSelector} .o-spreadsheet-grid-image[data-id='1']`); expect(firstTemplate).toHaveClass("o-spreadsheet-grid-selected"); // Navigate back to the previous template await contains(defaultTemplate).press("ArrowLeft"); expect(firstTemplate).not.toHaveClass("o-spreadsheet-grid-selected"); expect(defaultTemplate).toHaveClass("o-spreadsheet-grid-selected"); }); test("Can create a blank spreadsheet from template dialog in a specific folder", async function () { const mockDoAction = (action) => { expect.step("redirect"); expect(action.tag).toBe("action_open_spreadsheet"); }; await initTestEnvWithBlankSpreadsheet({ mockRPC: async function (route, args) { if ( args.model === "documents.document" && args.method === "action_open_new_spreadsheet" ) { expect(args.args[0].folder_id).toBe(1); expect.step("action_open_new_spreadsheet"); } }, }); mockActionService(mockDoAction); await contains(".o_search_panel_section").click(); await openTemplateDialog(); await select("1", { target: ".o-spreadsheet-templates-dialog select" }); await contains(`${dialogSelector} .o-spreadsheet-grid-image`).click(); await contains(`${dialogSelector} .o-spreadsheet-create`).click(); expect.verifySteps(["action_open_new_spreadsheet", "redirect"]); });