Files
enterprise-addons/documents/static/tests/kanban_view.test.js
T

1011 lines
39 KiB
JavaScript

import { describe, expect, test } from "@odoo/hoot";
import {
click,
keyDown,
press,
queryAll,
queryAllTexts,
setInputFiles,
waitFor,
waitForNone,
} from "@odoo/hoot-dom";
import { animationFrame } from "@odoo/hoot-mock";
import { inputFiles } from "@web/../tests/utils";
import { WebClient } from "@web/webclient/webclient";
import {
contains,
defineActions,
defineModels,
getService,
mockService,
mountWithCleanup,
onRpc,
patchWithCleanup,
toggleSearchBarMenu,
} from "@web/../tests/web_test_helpers";
import {
DocumentsModels,
getDocumentsTestServerModelsData,
makeDocumentRecordData,
mimetypeExamplesBase64,
} from "./helpers/data";
import { makeDocumentsMockEnv } from "./helpers/model";
import { embeddedActionsServerData } from "./helpers/test_server_data";
import { basicDocumentsKanbanArch, mountDocumentsKanbanView } from "./helpers/views/kanban";
import { getEnrichedSearchArch } from "./helpers/views/search";
import { documentsClientThumbnailService } from "@documents/views/helper/documents_client_thumbnail_service";
import { EventBus } from "@odoo/owl";
import { Deferred } from "@web/core/utils/concurrency";
describe.current.tags("desktop");
defineModels(DocumentsModels);
defineActions([
{
id: 1,
name: "Documents",
res_model: "documents.document",
views: [[false, "kanban"]],
},
{
id: 2,
name: "Documents",
res_model: "documents.document",
views: [[false, "kanban"]],
context: {
searchpanel_default_user_folder_id: 2,
},
},
]);
async function clickUploadButton() {
await contains("button.btn.btn-primary.o-dropdown:contains('New')").click();
await contains("button.btn.btn-link.o_documents_kanban_upload").click();
// This step seems necessary to succeed everytime vs. clicking on "Upload" above...
await contains("input.o_input_file.o_hidden", {
visible: false,
}).click();
await animationFrame();
await setInputFiles([new File(["fake_file"], "fake_file.tiff", { type: "text/plain" })]);
await animationFrame();
}
test("Open share with edit user_permission", async function () {
onRpc("/documents/touch/accessTokenFolder1", () => ({}));
const serverData = getDocumentsTestServerModelsData();
const { id: folder1Id, name: folder1Name } = serverData["documents.document"][0];
mockService("document.document", {
openSharingDialog: (documentIds) => {
expect(documentIds).toEqual([folder1Id]);
expect.step("open_share");
},
});
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
await contains(`.o_kanban_record:contains(${folder1Name}) .o_record_selector`).click({
ctrlKey: true,
});
await contains("button:contains(Share)").click();
expect.verifySteps(["open_share"]);
});
test("Colorless-tags are also visible on cards", async function () {
onRpc("/documents/touch/accessTokenFolder1", () => true);
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Testing tags", { folder_id: 1, tag_ids: [1, 2] }),
]);
const { name: folder1Name } = serverData["documents.document"][0];
const archWithTags = basicDocumentsKanbanArch.replace(
'<field name="name"/>',
'<field name="name"/>\n' +
'<field name="tag_ids" class="d-block text-wrap" widget="many2many_tags" options="{\'color_field\': \'color\'}"/>'
);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView({ arch: archWithTags });
await contains(`.o_kanban_record:contains(${folder1Name})`).click();
await animationFrame();
expect(
".o_kanban_record:contains('Testing tags') div[name='tag_ids'] div .o_tag:nth-of-type(1)"
).toHaveText("Colorless");
expect(
".o_kanban_record:contains('Testing tags') div[name='tag_ids'] div .o_tag:nth-of-type(2)"
).toHaveText("Colorful");
});
test("Uploading from control panel", async () => {
const _bus = new EventBus();
mockService("file_upload", {
bus: _bus,
upload: (route) => {
if (route.startsWith("/documents/upload")) {
_bus.trigger("FILE_UPLOAD_LOADED", {
upload: {
data: new FormData(),
xhr: { status: 200, response: '{ "records": [] }' },
},
});
expect.step("doc uploaded");
}
},
});
const serverData = getDocumentsTestServerModelsData();
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
await clickUploadButton();
expect.verifySteps(["doc uploaded"]);
});
test("Download button availability", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Request", { folder_id: 1 }),
makeDocumentRecordData(3, "Binary", { attachment_id: 1, folder_id: 1 }),
]);
serverData["ir.attachment"] = [{ id: 1, name: "binary" }];
const { name: folder1Name } = serverData["documents.document"][0];
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
await contains(`.o_kanban_record:contains(${folder1Name})`).click({ ctrlKey: true });
// Folder should be downloadable
await waitFor(".o_control_panel_actions:contains('Download')");
await contains(`.o_kanban_record:contains(${folder1Name})`).click({ ctrlKey: true });
// Request should not be downloadable
await contains(".o_kanban_record:contains('Request')").click();
await waitForNone(".o_control_panel_actions:contains('Download')");
// Binary should be downloadable
await contains(".o_kanban_record:contains('Binary')").click();
await waitFor(".o_control_panel_actions:contains('Download')");
// Multiple documents can be downloaded
await contains(`.o_kanban_record:contains(${folder1Name})`).click({ ctrlKey: true });
await waitFor(".o_control_panel_actions:contains('Download')");
// Button should remain even if some records are not downloadable
await contains(".o_kanban_record:contains('Request')").click({ ctrlKey: true });
await waitFor(".o_control_panel_actions:contains('Download')");
});
test("Drag and Drop - Search panel expand folders", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Sub Folder", { folder_id: 1, type: "folder" }),
makeDocumentRecordData(3, "Test Folder", { type: "folder" }),
]);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
const searchPanelSelector = ".o_search_panel_category_value .o_search_panel_label_title";
// Check that when we drag hover the Company folder it opens up to display its children
expect(queryAllTexts(searchPanelSelector)).toEqual([
"All",
"Company",
"My Drive",
"Shared with me",
"Recent",
"Trash",
]);
const { cancel, moveTo } = await contains(".o_kanban_record[data-value-id='3']").drag();
await moveTo(
".o_search_panel_category_value[data-value-id='COMPANY'] div.o_search_panel_label"
);
expect(queryAllTexts(searchPanelSelector)).toEqual([
"All",
"Company",
"Folder 1",
"Test Folder",
"My Drive",
"Shared with me",
"Recent",
"Trash",
]);
await moveTo(".o_search_panel_category_value[data-value-id='1'] div.o_search_panel_label");
expect(queryAllTexts(searchPanelSelector)).toEqual([
"All",
"Company",
"Folder 1",
"Sub Folder",
"Test Folder",
"My Drive",
"Shared with me",
"Recent",
"Trash",
]);
await cancel();
expect(queryAll(".o_record_temporary", { root: document.body })).toHaveCount(0, {
message: "temporary cards should have been cleaned up",
});
});
test("Drag and Drop - A folder into itself or its children", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Sub Folder", { folder_id: 1, type: "folder" }),
makeDocumentRecordData(3, "Folder 2", { type: "folder" }),
]);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
const folder1 = ".o_kanban_record[data-value-id='1']";
const folder2 = ".o_kanban_record[data-value-id='2']";
const folder3 = ".o_kanban_record[data-value-id='3']";
const { cancel, moveTo } = await contains(folder1).drag();
await moveTo(folder2);
expect(folder2).toHaveClass("o_drag_invalid");
expect(".o_documents_dnd_text").toHaveText(
"You cannot move a folder into itself or a children."
);
await moveTo(folder3);
expect(folder3).toHaveClass("o_drag_hover");
expect(".o_documents_dnd_text").toHaveText("Folder 1");
await moveTo(folder1);
expect(folder1).toHaveClass("o_drag_invalid");
expect(".o_documents_dnd_text").toHaveText(
"You cannot move a folder into itself or a children."
);
await cancel();
});
test("Drag and Drop - After selecting multiple documents", async function () {
const serverData = getDocumentsTestServerModelsData(
[1, 2, 3].map((idx) =>
makeDocumentRecordData(idx + 1, `Test Document ${idx}`, { folder_id: 1 })
)
);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
const document2 = ".o_kanban_record[data-value-id='2']";
const document4 = ".o_kanban_record[data-value-id='4']";
await contains(document2).click({ ctrlKey: true });
await contains(document4).click({ ctrlKey: true });
let { cancel } = await contains(document2).drag();
expect(document2).toHaveStyle({ opacity: "0.3" });
expect(document4).toHaveStyle({ opacity: "0.3" });
expect(".o_documents_dnd_text").toHaveText("Test Document 1");
await cancel();
({ cancel } = await contains(document4).drag());
expect(document2).toHaveStyle({ opacity: "0.3" });
expect(document4).toHaveStyle({ opacity: "0.3" });
expect(".o_documents_dnd_text").toHaveText("Test Document 3");
await cancel();
});
test("Drag and Drop - Check permission when dropping documents", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Test Document 1"),
makeDocumentRecordData(3, "Test Document 2", { user_permission: "view" }),
]);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
let { drop, moveTo } = await contains(".o_kanban_record[data-value-id='2']").drag();
await moveTo(".o_kanban_record[data-value-id='1']");
await drop();
await waitFor(".o_notification");
expect(".o_notification_content:eq(-1)").toHaveText("The document has been moved.");
({ drop, moveTo } = await contains(".o_kanban_record[data-value-id='3']").drag());
await moveTo(".o_kanban_record[data-value-id='1']");
await drop();
await waitFor(".o_notification");
expect(".o_notification_content:eq(-1)").toHaveText(
"At least one document could not be moved due to access rights."
);
});
test("Drag and Drop - Check access rights confirmation popup when moving from kanban view", async function () {
onRpc("documents.document", "write", ({ args }) => {
expect(args[0][0]).toBe(2);
expect(args[1].user_folder_id).toBe("5");
expect.step("action_move_documents");
});
const documents = [
[2, "Internal Viewer - Link None - Discoverable", "view", "none", false],
[3, "Internal Editor - Link None - Discoverable", "edit", "none", false],
[4, "Internal Viewer - Link Viewer - Discoverable", "view", "view", false],
[5, "Internal Viewer - Link None - Must have link", "view", "none", true],
[6, "Internal Viewer - Link Viewer - Must have link", "view", "view", true],
];
const serverData = getDocumentsTestServerModelsData(
documents.map(([id, name, access_internal, access_via_link, is_access_via_link_hidden]) =>
makeDocumentRecordData(id, name, {
access_internal,
access_via_link,
is_access_via_link_hidden,
type: "folder",
})
)
);
const cases = [
[2, 3, true], // Change internal access
[2, 4, true], // Change link access
[2, 5, false], // Change link hidden access with link access == none
[4, 6, true], // Change link hidden access with link access != none
];
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
for (const [docToMove, targetDoc, expectedConfirmation] of cases) {
const { drop, moveTo } = await contains(
`.o_kanban_record[data-value-id='${docToMove}']`
).drag();
await moveTo(`.o_kanban_record[data-value-id='${targetDoc}']`);
await drop();
if (expectedConfirmation) {
// Wait for dialog, cancel move and close dialog
await waitFor(".o_dialog:not(.o_inactive_modal)");
expect(".o_dialog:not(.o_inactive_modal)").toHaveCount(1);
await click(".o_dialog:not(.o_inactive_modal) .modal-footer button:contains(Cancel)");
await animationFrame();
expect(".o_dialog:not(.o_inactive_modal)").toHaveCount(0);
} else {
// Assert move
await animationFrame();
expect.verifySteps(["action_move_documents"]);
}
}
});
test("Drag and Drop - Check access rights confirmation popup when moving from search panel", async function () {
onRpc("action_move_folder", ({ args }) => {
expect.step(`action_move_folder_${args[0][0]}_${args[1]}`);
expect(typeof args[1]).toBe("string");
});
const documents = [
[2, "Internal Viewer - Link None - Discoverable", "view", "none", false],
[3, "Internal Editor - Link None - Discoverable", "edit", "none", false],
[4, "Internal Viewer - Link Viewer - Discoverable", "view", "view", false],
[5, "Internal Viewer - Link None - Must have link", "view", "none", true],
[6, "Internal Viewer - Link Viewer - Must have link", "view", "view", true],
];
const labelByCode = { none: "None", view: "Viewer", edit: "Editor" };
const serverData = getDocumentsTestServerModelsData(
documents.map(([id, name, access_internal, access_via_link, is_access_via_link_hidden]) =>
makeDocumentRecordData(id, name, {
access_internal,
access_via_link,
is_access_via_link_hidden,
type: "folder",
})
)
);
const cases = [
[2, 3, true], // Change internal access
[2, 4, true], // Change link access
[2, 5, false], // Change link hidden access with link access == none
[4, 6, true], // Change link hidden access with link access != none
];
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
await click(".o_search_panel_category_value[data-value-id='COMPANY'] .o_toggle_fold");
await animationFrame();
for (const [docToMove, targetDoc, expectedConfirmation] of cases) {
// Drag & Drop under the target folder
const { drop, moveTo } = await contains(
`.o_search_panel_category_value[data-value-id='${docToMove}']`
).drag();
const targetFolder = document.querySelector(
`.o_search_panel_category_value[data-value-id='${targetDoc}']`
);
await moveTo(targetFolder);
await moveTo(targetFolder, {
position: { y: targetFolder.offsetHeight },
relative: true,
});
await moveTo(targetFolder, {
position: { x: targetFolder.offsetWidth },
relative: true,
});
await drop();
await animationFrame();
if (expectedConfirmation) {
await waitFor(".o_dialog:not(.o_inactive_modal)");
expect(".o_dialog:not(.o_inactive_modal)").toHaveCount(1);
const targetFolder = documents.find((doc) => doc[0] === targetDoc);
const accessInternal = labelByCode[targetFolder[2]];
const accessViaLink = labelByCode[targetFolder[3]];
expect(
`[aria-labelledby="o_documents_access_update_confirmation_access_internal"]:contains(${accessInternal})`
).toBeVisible();
expect(
`[aria-labelledby="o_documents_access_update_confirmation_access_via_link"]:contains(${accessViaLink})`
).toBeVisible();
await contains(
".o_dialog:not(.o_inactive_modal) .modal-footer button:contains(Cancel)"
).click();
expect.step(`confirm_${docToMove}_${targetDoc}`);
await waitForNone(".o_dialog:not(.o_inactive_modal)");
}
}
// Dropping inside COMPANY
const source = contains(".o_search_panel_category_value[data-value-id='4']");
const { drop, moveTo } = await source.drag();
await moveTo(document.querySelector(".o_search_panel_category_value[data-value-id='1']"));
await drop();
await animationFrame();
expect.verifySteps([
"confirm_2_3",
"confirm_2_4",
"action_move_folder_2_5",
"confirm_4_6",
"action_move_folder_4_COMPANY",
]);
});
test("Drag and Drop - Drop multiple documents at once", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Test Document 1"),
makeDocumentRecordData(3, "Test Document 2", { user_permission: "view" }),
]);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
await contains(".o_kanban_record[data-value-id='2']").click({ ctrlKey: true });
await contains(".o_kanban_record[data-value-id='3']").click({ ctrlKey: true });
const { drop, moveTo } = await contains(".o_kanban_record[data-value-id='2']").drag();
await moveTo(".o_kanban_record[data-value-id='1']");
await drop();
await waitFor(".o_notification");
expect(queryAllTexts(".o_notification_content")).toEqual([
"At least one document could not be moved due to access rights.",
"The document has been moved.",
]);
});
test("Drag and Drop - Drop document while holding CTRL", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Test Document"),
]);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
const { drop, moveTo } = await contains(".o_kanban_record[data-value-id='2']").drag();
expect(".o_documents_dnd_modifier").not.toBeVisible();
await keyDown("Control");
expect(".o_documents_dnd_modifier").toBeVisible();
await moveTo(
".o_search_panel_category_value[data-value-id='COMPANY'] div.o_search_panel_label"
);
await moveTo(".o_search_panel_category_value[data-value-id='1'] div.o_search_panel_label");
expect(".o_documents_dnd_modifier").toBeVisible(); // check after moveTo to be sure it's still visible
await drop();
await waitFor(".o_notification");
expect(".o_notification_content:eq(-1)").toHaveText("A shortcut has been created.");
});
test("Lock action availability and check", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Binary", { folder_id: 1 }),
]);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
const folder = ".o_kanban_record[data-value-id='1']";
// Folder should not be lockable
await contains(folder).click({ ctrlKey: true });
await contains(".o_cp_action_menus button").click();
await waitForNone(".o-dropdown--menu .o-dropdown-item:contains('Lock')");
// Binary should be lockable
await contains(".o_kanban_record:contains('Binary')").click();
await contains(".o_cp_action_menus button").click();
await contains(".o-dropdown--menu .o-dropdown-item:contains('Lock')").click();
await waitFor(".o_kanban_record i.fa-lock");
// Unlock the binary record
await contains(".o_cp_action_menus button").click();
await contains(".o-dropdown--menu .o-dropdown-item:contains('Unlock')").click();
expect(".modal-body").toHaveText(
"This document is locked by OdooBot.\nAre you sure you want to unlock it?"
);
await contains(".modal .modal-footer .btn-primary").click();
await waitForNone(".o_kanban_record i.fa-lock");
// Multiple documents cannot be locked
await contains(folder).click({ ctrlKey: true });
await contains(".o_cp_action_menus button").click();
await waitForNone(".o-dropdown--menu .o-dropdown-item:contains('Lock')");
});
test("only show common available actions", async function () {
await makeDocumentsMockEnv({ serverData: embeddedActionsServerData });
await mountDocumentsKanbanView();
await contains(`.o_kanban_record:contains('Request 1')`).click();
await waitFor(".o_control_panel_actions:contains('Action 1')");
await contains(`.o_kanban_record:contains('Request 2')`).click();
await waitForNone(".o_control_panel_actions:contains('Action 1')");
await waitFor(".o_control_panel_actions:contains('Action 2 only')");
await waitFor(".o_control_panel_actions:contains('Action 2 and 3')");
await contains(`.o_kanban_record:contains('Request 3')`).click({ ctrlKey: true });
await waitForNone(".o_control_panel_actions:contains('Action 2 only')");
await waitFor(".o_control_panel_actions:contains('Action 2 and 3')");
});
test("Thumbnail: webp thumbnail generation", async function () {
onRpc("/documents/document/3/update_thumbnail", async (args) => {
const { params } = await args.json();
expect.step("thumbnail generated");
expect(params.thumbnail.startsWith("/9j/")).toEqual(true);
return true;
});
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(3, "Test Document", {
thumbnail_status: "client_generated",
attachment_id: 2,
folder_id: 1,
mimetype: "image/webp",
}),
]);
serverData["ir.attachment"] = [{ id: 2, name: "binary" }];
await makeDocumentsMockEnv({ serverData });
patchWithCleanup(documentsClientThumbnailService, {
_getLoadedImage() {
const img = new Image();
const imagePromise = new Deferred();
img.onload = () => imagePromise.resolve(img);
img.src = "data:image/webp;base64," + mimetypeExamplesBase64.WEBP;
return imagePromise;
},
});
await mountDocumentsKanbanView();
expect.verifySteps(["thumbnail generated"]);
});
test("Document Request Upload", async function () {
mockService("file_upload", {
upload: (route, files, params) => {
if (route === "/documents/upload/accessToken") {
expect.step("upload_done");
}
},
});
const serverData = getDocumentsTestServerModelsData([
{
folder_id: 1,
id: 2,
name: "Test Request",
access_token: "accessToken",
},
]);
const archWithRequest = basicDocumentsKanbanArch.replace(
'<field name="name"/>',
'<field name="name"/>\n' +
'<t t-set="isRequest" t-value="record.type.raw_value === \'binary\' and !record.attachment_id.raw_value"/>\n' +
'<input t-if="isRequest" type="file" class="o_hidden o_kanban_replace_document"/>\n'
);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView({ arch: archWithRequest });
const file = new File(["hello world"], "text.txt", { type: "text/plain" });
await inputFiles("input.o_kanban_replace_document", [file]);
await animationFrame();
expect.verifySteps(["upload_done"]);
});
test("Document Request Upload no user_folder_id if access_token", async function () {
mockService("file_upload", {
upload: (route, files, params) => {
if (route === "/documents/upload/accessToken") {
expect.step("upload_done");
const data = new FormData();
params.buildFormData(data);
if (!data.get("user_folder_id")) {
expect.step("no_user_folder_id");
}
}
},
});
const serverData = getDocumentsTestServerModelsData([
{
folder_id: 1,
id: 2,
name: "Test Request",
access_token: "accessToken",
},
]);
const archWithRequest = basicDocumentsKanbanArch.replace(
'<field name="name"/>',
'<field name="name"/>\n' +
'<t t-set="isRequest" t-value="record.type.raw_value === \'binary\' and !record.attachment_id.raw_value"/>\n' +
'<input t-if="isRequest" type="file" class="o_hidden o_kanban_replace_document"/>\n'
);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView({ arch: archWithRequest });
const file = new File(["hello world"], "text.txt", { type: "text/plain" });
await inputFiles("input.o_kanban_replace_document", [file]);
await animationFrame();
expect.verifySteps(["upload_done", "no_user_folder_id"]);
});
test("focus when selecting all - ctrl + a", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Test Document", { folder_id: 1 }),
makeDocumentRecordData(3, "Test Document 2", { folder_id: 1 }),
]);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
await contains(".o_kanban_renderer").click();
await keyDown(["Control", "a"]);
await waitFor(".o_kanban_record[data-value-id='1']:focus");
await waitFor(".o_selection_box");
expect(".o_record_selected").toHaveCount(3);
await keyDown(["Control", "a"]);
await waitFor(".o_kanban_record[data-value-id='1']:focus");
await waitFor(".o_searchview");
expect(".o_record_selected").toHaveCount(0);
// Focus another document first
await contains(".o_kanban_record[data-value-id='3']").click();
await keyDown(["Control", "a"]);
await waitFor(".o_kanban_record[data-value-id='3']:focus");
await keyDown(["Control", "a"]);
await waitFor(".o_kanban_record[data-value-id='3']:focus");
});
test.tags("desktop");
test("document selector: include archived checkbox should not be shown", async () => {
await mountDocumentsKanbanView();
await toggleSearchBarMenu();
await contains(".o_filter_menu .dropdown-item").click();
await waitFor(".o_tree_editor_condition");
expect(".form-switch label:contains(Include archived)").not.toHaveCount();
});
test("Split PDF button availability", async function () {
const serverData = getDocumentsTestServerModelsData([
{
attachment_id: 1,
id: 2,
name: "text_file.txt",
user_permission: "edit",
mimetype: "image/webp",
},
{
attachment_id: 2,
id: 3,
name: "pdf1.pdf",
user_permission: "view",
mimetype: "application/pdf",
},
{
attachment_id: 3,
id: 4,
name: "pdf2.pdf",
user_permission: "edit",
mimetype: "application/pdf",
},
]);
serverData["ir.attachment"] = [
{ id: 1, name: "text_file.txt", mimetype: "image/webp" },
{ id: 2, name: "pdf1.pdf", mimetype: "application/pdf" },
{ id: 3, name: "pdf2.pdf", mimetype: "application/pdf" },
];
DocumentsModels.DocumentsDocument._views = {
kanban: basicDocumentsKanbanArch,
[["search", false]]: getEnrichedSearchArch(),
};
await makeDocumentsMockEnv({ serverData });
await mountWithCleanup(WebClient);
await getService("action").doAction(1);
// Non-PDF with edit permission in control panel
await contains(".o_kanban_record:contains('text_file.txt') .o_record_selector").click();
await contains(".o_dropdown_title").click();
await waitForNone(".o-dropdown-item:contains('Split PDF')");
// Non-PDF with edit permission in preview
await contains(".o_kanban_record:contains('text_file.txt') [name='document_preview']").click();
await contains(".o-FileViewer .o_cp_action_menus .o-dropdown").click();
await waitForNone(".o-dropdown-item:contains('Split PDF')");
await press("escape");
await waitForNone(".o-FileViewer");
// PDF with view permission in control panel
await contains(".o_kanban_record:contains('pdf1.pdf') .o_record_selector").click();
await contains(".o_dropdown_title").click();
await waitForNone(".o-dropdown-item:contains('Split PDF')");
// PDF with view permission in preview
await contains(".o_kanban_record:contains('pdf1.pdf') [name='document_preview']").click();
await contains(".o-FileViewer .o_cp_action_menus .o-dropdown").click();
await waitForNone(".o-dropdown-item:contains('Split PDF')");
await press("escape");
await waitForNone(".o-FileViewer");
// PDF with edit permission in control panel
await contains(".o_kanban_record:contains('pdf2.pdf') .o_record_selector").click();
await contains(".o_dropdown_title").click();
await waitFor(".o-dropdown-item:contains('Split PDF')");
// PDF with edit permission in preview
await contains(".o_kanban_record:contains('pdf2.pdf') [name='document_preview']").click();
await contains(".o-FileViewer .o_cp_action_menus .o-dropdown").click();
await waitFor(".o-dropdown-item:contains('Split PDF')");
});
test("Export action is not available in file viewer ", async function () {
const serverData = getDocumentsTestServerModelsData([
{
folder_id: 1,
id: 2,
url: "https://youtu.be/Ayab6wZ_U1A",
type: "url",
},
]);
const archWithURL = basicDocumentsKanbanArch.replace(
'<field name="name"/>',
'<field name="name"/>\n' + '<field name="url"/>'
);
DocumentsModels.DocumentsDocument._views = {
kanban: archWithURL,
[["search", false]]: getEnrichedSearchArch(),
};
await makeDocumentsMockEnv({ serverData });
await mountWithCleanup(WebClient);
await getService("action").doAction(1);
await contains(
".o_kanban_record:contains('https://youtu.be/Ayab6wZ_U1A') .o_record_selector"
).click();
await contains(".o_dropdown_title").click();
await waitFor(".o-dropdown-item:contains('Export')");
await contains(
".o_kanban_record:contains('https://youtu.be/Ayab6wZ_U1A') [name='document_preview']"
).click();
await contains(".o-FileViewer .o_cp_action_menus .o-dropdown").click();
await waitForNone(".o-dropdown-item:contains('Export')");
});
test("Control panel cog menu visibility", async function () {
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Request", { folder_id: 1 }),
]);
const { name: folder1Name } = serverData["documents.document"][0];
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
await waitFor(".o_kanban_renderer");
await contains(".o_search_panel_label_title:contains('Company')").click();
await waitFor(".o_last_breadcrumb_item:contains('Company')");
// There should be no cog menu on Company
await waitForNone(".o_cp_action_menus", {});
// There should be one on Company roots
await contains(`.o_kanban_record:contains('${folder1Name}')`).click();
await waitFor(`.o_last_breadcrumb_item:contains('${folder1Name}')`);
expect(`.o_cp_action_menus`).toHaveCount(1);
});
test("Select a range with SHIFT key", async () => {
await makeDocumentsMockEnv({ serverData: embeddedActionsServerData });
await mountDocumentsKanbanView();
const { name: folder1Name } = embeddedActionsServerData["documents.document"][0];
await contains(`.o_kanban_record:contains(${folder1Name}) .o_record_selector`).click({
ctrlKey: true,
});
await keyDown("Shift");
await contains(".o_kanban_record:contains(Request 2)").click();
expect(".o_kanban_record:contains(Request 3)").toHaveClass("o_record_selected");
expect("div.o_record_selected").toHaveCount(3);
});
test("Name in previewer is correct without attachment", async function () {
const serverData = getDocumentsTestServerModelsData([
{
id: 10,
name: "Shin chan: The Spicy Kasukabe",
type: "url",
url: "https://www.youtube.com/watch?v=Qv_-R9kw5eg",
mimetype: "text/html",
folder_id: 1,
},
{
id: 11,
name: "Mom vs Dad |Shinchan",
type: "url",
url: "https://www.youtube.com/watch?v=sZeU-nrm8UA",
mimetype: "text/html",
folder_id: 1,
},
]);
patchWithCleanup(HTMLIFrameElement.prototype, {
contentWindow: {
get: () => null,
},
contentDocument: {
get: () => null,
},
});
const previewedAttachments = [];
mockService("document.document", {
setPreviewedDocument: (doc) => {
if (doc && doc.attachment) {
previewedAttachments.push({
id: doc.attachment.id,
name: doc.attachment.name,
url: doc.attachment.url,
documentId: doc.attachment.documentId,
});
expect.step(`preview_${doc.attachment.name}`);
}
},
documentList: null,
});
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView();
await contains(".o_kanban_record:contains('Shin chan') [name='document_preview']").click();
await waitFor(".o-FileViewer");
const closeBtn = document.querySelector(".o-FileViewer [aria-label='Close']");
closeBtn.click();
await waitForNone(".o-FileViewer");
await contains(".o_kanban_record:contains('Mom vs Dad') [name='document_preview']").click();
await waitFor(".o-FileViewer");
expect(previewedAttachments).toHaveLength(2);
expect(previewedAttachments[0].id).toBe(-10);
expect(previewedAttachments[0].name).toBe("Shin chan: The Spicy Kasukabe");
expect(previewedAttachments[1].id).toBe(-11);
expect(previewedAttachments[1].name).toBe("Mom vs Dad |Shinchan");
expect.verifySteps(["preview_Shin chan: The Spicy Kasukabe", "preview_Mom vs Dad |Shinchan"]);
});
test("Check actions with preview", async function () {
const serverData = getDocumentsTestServerModelsData([
{
attachment_id: 1,
id: 2,
name: "Test_file.txt",
mimetype: "image/webp",
},
]);
serverData["ir.attachment"] = [{ id: 1, name: "Test_file.txt", mimetype: "image/webp" }];
const basicDocumentsKanbanArchWithLockUid = basicDocumentsKanbanArch.replace(
'<field name="name"/>',
'<field name="name"/>\n<field name="lock_uid"/>'
);
await makeDocumentsMockEnv({ serverData });
await mountDocumentsKanbanView({ arch: basicDocumentsKanbanArchWithLockUid });
// Document is not locked so there should be Lock option.
await contains(".o_kanban_record:contains('Test_file.txt') [name='document_preview']").click();
await contains(".o-FileViewer .o_cp_action_menus .o-dropdown").click();
await waitFor(".o-dropdown-item:contains('Lock')");
await contains(".o-dropdown-item:contains('Lock')").click();
// The preview should be closed when clicking the lock action.
expect(".o-FileViewer").toHaveCount(0);
// Document is locked so there should be Unlock option.
await contains(".o_kanban_record:contains('Test_file.txt') [name='document_preview']").click();
await contains(".o-FileViewer .o_cp_action_menus .o-dropdown").click();
await waitFor(".o-dropdown-item:contains('Unlock')");
});
test("Ensure previewer shows correct name after renaming a document", async function () {
const serverData = getDocumentsTestServerModelsData([
{
attachment_id: 1,
id: 2,
name: "text_file.txt",
mimetype: "image/webp",
},
]);
DocumentsModels["DocumentsDocument"]._views = {
kanban: basicDocumentsKanbanArch,
search: getEnrichedSearchArch(),
form: "<form><field name='name'/></form>",
};
serverData["ir.attachment"] = [{ id: 1, name: "text_file.txt", mimetype: "image/webp" }];
onRpc("ir.model", "display_name_for", ({ args }) =>
args[0].map((model) => ({ model, display_name: model }))
);
await makeDocumentsMockEnv({ serverData });
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "documents.document",
type: "ir.actions.act_window",
views: [[false, "kanban"]],
});
await contains(".o_kanban_record:contains('text_file.txt')").click({ ctrlKey: true });
await contains(".o_control_panel_actions button:contains('Action')").click();
await contains(".o-dropdown-item:contains('Rename')").click();
await contains(".o_input").edit("test1.txt");
await contains(".o_form_button_save:contains('Save')").click();
expect(".o_kanban_record span:contains('test1.txt')").toHaveCount(1);
await contains(".o_kanban_record:contains('test1.txt') [name='document_preview']").click();
await waitFor(".o-FileViewer");
expect(".o-FileViewer-header span:contains('test1.txt')").toHaveCount(1);
// Document should open after renaming
await contains(".o_dropdown_title").click();
await waitFor(".fa-info-circle");
await contains(".fa-info-circle").click();
await waitFor(".o_documents_details_panel");
await contains(".o_documents_details_panel .o_documents_details_panel_name input").edit(
"Renamed_Test_file.txt"
);
await contains(".o_control_panel_navigation .fa-close").click();
await waitForNone(".o_documents_details_panel");
await contains(".o-FileViewer [aria-label='Close']").click();
await waitForNone(".o-FileViewer");
await contains(
".o_kanban_record:contains('Renamed_Test_file.txt') [name='document_preview']"
).click();
await waitFor(".o-FileViewer");
expect(".o-FileViewer-header").toHaveText("Renamed_Test_file.txt");
});
test("Uploading to 'All' from a bridge uploads to the bridge's default folder not 'My Drive'", async function () {
onRpc("/documents/touch/<access_token>", () => ({}));
const serverData = getDocumentsTestServerModelsData([
makeDocumentRecordData(2, "Test", { type: "folder", user_permission: "edit" }),
]);
mockService("file_upload", {
upload: (route, ...args) => {
if (route.startsWith("/documents/upload/accessTokenTest")) {
expect.step("correct_folder");
}
},
});
DocumentsModels["DocumentsDocument"]._views = {
kanban: basicDocumentsKanbanArch,
search: getEnrichedSearchArch(),
};
await makeDocumentsMockEnv({ serverData });
await mountWithCleanup(WebClient);
await getService("action").doAction(2);
await contains("span.o_search_panel_label_title:contains('All')").click();
await clickUploadButton();
expect.verifySteps(["correct_folder"]);
});