381 lines
16 KiB
JavaScript
381 lines
16 KiB
JavaScript
import { describe, expect, test } from "@odoo/hoot";
|
|
import { waitFor, waitForNone } from "@odoo/hoot-dom";
|
|
import { animationFrame } from "@odoo/hoot-mock";
|
|
import {
|
|
contains,
|
|
defineModels,
|
|
mockService,
|
|
onRpc,
|
|
patchWithCleanup,
|
|
selectFieldDropdownItem,
|
|
serverState,
|
|
} from "@web/../tests/web_test_helpers";
|
|
import { user } from "@web/core/user";
|
|
|
|
import {
|
|
DocumentsModels,
|
|
getDocumentsTestServerModelsData,
|
|
makeDocumentRecordData,
|
|
} from "./helpers/data";
|
|
import { makeDocumentsMockEnv } from "./helpers/model";
|
|
import { embeddedActionsServerData } from "./helpers/test_server_data";
|
|
import { mountDocumentsListView } from "./helpers/views/list";
|
|
|
|
describe.current.tags("desktop");
|
|
|
|
defineModels(DocumentsModels);
|
|
|
|
/**
|
|
* Shortcut for details panel selector
|
|
* @param selector
|
|
* @return {`.o_documents_details_panel ${string}`}
|
|
*/
|
|
const dp = (selector) => `.o_documents_details_panel ${selector}`;
|
|
|
|
test("Open share with view 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 mountDocumentsListView();
|
|
await contains(`.o_data_row:contains(${folder1Name}) .o_list_record_selector`).click();
|
|
await contains("button:contains(Share)").click();
|
|
|
|
expect.verifySteps(["open_share"]);
|
|
});
|
|
|
|
test("Right panel shows and updates focused or container record only", async function () {
|
|
onRpc("/documents/touch/<access_token>", () => ({}));
|
|
|
|
const file2Id = 3;
|
|
const serverData = getDocumentsTestServerModelsData([
|
|
makeDocumentRecordData(2, "File 1", { attachment_id: 1, folder_id: 1 }),
|
|
makeDocumentRecordData(file2Id, "File 2", { attachment_id: 2, folder_id: 1 }),
|
|
makeDocumentRecordData(4, "File 3", { attachment_id: 3, folder_id: 1 }),
|
|
]);
|
|
serverData["ir.attachment"] = [
|
|
{ id: 1, name: "One" },
|
|
{ id: 2, name: "Two" },
|
|
{ id: 3, name: "Three" },
|
|
];
|
|
const { name: folder1Name } = serverData["documents.document"][0];
|
|
onRpc("web_save", ({ args }) => {
|
|
if (args[0].length === 1 && args[0][0] === file2Id) {
|
|
expect.step("edit_request_2");
|
|
}
|
|
});
|
|
onRpc("ir.model", "display_name_for", ({ args }) =>
|
|
args[0].map((model) => ({ model, display_name: model }))
|
|
);
|
|
await makeDocumentsMockEnv({ serverData });
|
|
await mountDocumentsListView();
|
|
|
|
// Open right panel
|
|
await contains(".o_control_panel_navigation .fa-info-circle").click();
|
|
await waitFor(".documents_chatter_disabled_overlay");
|
|
// Focus without selection
|
|
await contains(`.o_data_row td[name='name']:contains(${folder1Name})`).click();
|
|
await animationFrame();
|
|
expect(dp(".o_documents_details_panel_name input")).toHaveValue(folder1Name);
|
|
await contains(`.o_list_renderer`).click(); // de-focus
|
|
|
|
await waitFor(".documents_chatter_disabled_overlay"); // As we're in all/company
|
|
// Enter folder
|
|
await contains(`.o_data_row:contains(${folder1Name}) .fa-folder-o`).click();
|
|
await animationFrame();
|
|
expect(`.o_data_row .o_list_record_selector`).toHaveCount(3);
|
|
expect(dp(".o_documents_details_panel_name input")).toHaveValue(folder1Name);
|
|
|
|
// Focus without selection
|
|
await contains(".o_data_row :contains('File 1')").click();
|
|
expect(dp(".o_documents_details_panel_name input")).toHaveValue("File 1");
|
|
// Unfocus
|
|
await contains(`.o_list_renderer`).click();
|
|
expect(dp(".o_documents_details_panel_name input")).toHaveValue(folder1Name);
|
|
|
|
// select record focuses it
|
|
await contains(".o_data_row:contains('File 1') .o_list_record_selector").click();
|
|
expect(dp(".o_documents_details_panel_name input")).toHaveValue("File 1");
|
|
// Focus without selection
|
|
await contains(".o_data_row :contains('File 2')").click();
|
|
expect(dp(".o_documents_details_panel_name input")).toHaveValue("File 2");
|
|
// Editing unselected File 2 only
|
|
await contains(dp(".o_documents_details_panel_name input")).edit("File 4");
|
|
// Row is modified, not File 1
|
|
await waitFor(".o_data_row :contains('File 4')");
|
|
await waitFor(".o_data_row :contains('File 1')");
|
|
|
|
expect.verifySteps(["edit_request_2"]);
|
|
});
|
|
|
|
test("Document actions are hidden when focused record is not selected", async function () {
|
|
onRpc("/documents/touch/<access_token>", () => ({}));
|
|
onRpc("ir.model", "display_name_for", ({ args }) =>
|
|
args[0].map((model) => ({ model, display_name: model }))
|
|
);
|
|
|
|
const serverData = getDocumentsTestServerModelsData([
|
|
makeDocumentRecordData(2, "File 1", { attachment_id: 1, folder_id: 1 }),
|
|
makeDocumentRecordData(3, "File 2", { attachment_id: 2, folder_id: 1 }),
|
|
]);
|
|
serverData["ir.attachment"] = [
|
|
{ id: 1, name: "One" },
|
|
{ id: 2, name: "Two" },
|
|
];
|
|
await makeDocumentsMockEnv({ serverData });
|
|
await mountDocumentsListView();
|
|
// select record focuses it
|
|
await contains(".o_data_row:contains('File 1') .o_list_record_selector").click();
|
|
// Actions are visible as selection is focused
|
|
await waitFor(".o_control_panel_actions:contains('Download')");
|
|
// Focus without selection
|
|
await contains(".o_data_row :contains('File 2')").click();
|
|
await waitFor(".o_selection_container");
|
|
// Actions are no longer visible as focused is not selected
|
|
await waitFor(".o_control_panel_actions:not(:contains('Download'))");
|
|
// Select it to show actions again
|
|
await contains(".o_data_row:contains('File 2') .o_list_record_selector").click();
|
|
await waitFor(".o_control_panel_actions:contains('Download')");
|
|
});
|
|
|
|
test("Actions menu opens after selecting multiple records", async function () {
|
|
onRpc("/documents/touch/<access_token>", () => ({}));
|
|
|
|
const serverData = getDocumentsTestServerModelsData([
|
|
makeDocumentRecordData(2, "File 1", { attachment_id: 1, folder_id: 1 }),
|
|
makeDocumentRecordData(3, "File 2", { attachment_id: 2, folder_id: 1 }),
|
|
]);
|
|
serverData["ir.attachment"] = [
|
|
{ id: 1, name: "One" },
|
|
{ id: 2, name: "Two" },
|
|
];
|
|
await makeDocumentsMockEnv({ serverData });
|
|
await mountDocumentsListView();
|
|
await contains(".o_data_row:contains('File 1') .o_list_record_selector").click();
|
|
await contains(".o_data_row:contains('File 2') .o_list_record_selector").click();
|
|
await waitFor(".o_selection_box:contains('2')");
|
|
await contains(".o_cp_action_menus button:contains('Actions')").click();
|
|
await waitFor(".o-dropdown--menu .o_menu_item");
|
|
});
|
|
|
|
test("only show common available actions", async function () {
|
|
await makeDocumentsMockEnv({ serverData: embeddedActionsServerData });
|
|
await mountDocumentsListView();
|
|
|
|
await contains(`.o_data_row:contains('Request 1') .o_list_record_selector`).click();
|
|
await waitFor(".o_control_panel_actions:contains('Action 1')");
|
|
await contains(`.o_data_row:contains('Request 1') .o_list_record_selector`).click();
|
|
|
|
await contains(`.o_data_row:contains('Request 2') .o_list_record_selector`).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_data_row:contains('Request 3') .o_list_record_selector`).click();
|
|
await waitForNone(".o_control_panel_actions:contains('Action 2 only')");
|
|
await waitFor(".o_control_panel_actions:contains('Action 2 and 3')");
|
|
});
|
|
|
|
test("Required document name", async function () {
|
|
const serverData = getDocumentsTestServerModelsData([
|
|
makeDocumentRecordData(2, "Testing file", { folder_id: 1 }),
|
|
makeDocumentRecordData(3, "Testing folder", { folder_id: 1 }),
|
|
]);
|
|
await makeDocumentsMockEnv({ serverData });
|
|
await mountDocumentsListView();
|
|
const lr = (documentName, selector) => `.o_data_row:contains('${documentName}') ${selector}`;
|
|
for (const documentName of ["Testing folder", "Testing file"]) {
|
|
await contains(lr(documentName, ".o_list_record_selector")).click();
|
|
await contains(lr(documentName, ".o_data_cell[name='name']")).click();
|
|
await expect(lr(documentName, ".o_data_cell[name='name'] input")).toHaveCount(1);
|
|
await expect(lr(documentName, ".o_data_cell[name='name'] input")).toHaveValue(documentName);
|
|
// Set empty name
|
|
await contains(lr(documentName, ".o_data_cell[name='name'] input")).edit("");
|
|
await animationFrame();
|
|
expect(".o_notification").toHaveCount(1);
|
|
expect(".o_notification").toHaveText("Name cannot be empty.");
|
|
await contains(".o_notification .o_notification_close").click();
|
|
await expect(lr(documentName, ".o_data_cell[name='name']")).toHaveText(documentName);
|
|
await expect(lr(documentName, ".o_data_cell[name='name'] input")).toHaveCount(0);
|
|
// Remove selection and close record edition
|
|
await contains(".o_list_renderer").click();
|
|
await animationFrame();
|
|
}
|
|
});
|
|
|
|
test("documents list: don't unselect all when interacting with the headers", async () => {
|
|
await makeDocumentsMockEnv({ serverData: embeddedActionsServerData });
|
|
await mountDocumentsListView();
|
|
|
|
await contains(".o_data_row:eq(0) .o_list_record_selector input").click();
|
|
await contains(".o_data_row:eq(1) .o_list_record_selector input").click();
|
|
|
|
expect(".o_data_row_selected").toHaveCount(2);
|
|
|
|
await contains("th:eq(2) .o_resize", { visible: false }).dragAndDrop("th:eq(3)");
|
|
|
|
expect(".o_data_row_selected").toHaveCount(2);
|
|
});
|
|
|
|
test("company_id field visibility for internal in multicompany", async function () {
|
|
serverState.companies = [
|
|
{ id: 1, name: "Company 1", sequence: 1, parent_id: false, child_ids: [] },
|
|
{ id: 2, name: "Company 2", sequence: 2, parent_id: false, child_ids: [] },
|
|
];
|
|
const serverData = getDocumentsTestServerModelsData();
|
|
await makeDocumentsMockEnv({ serverData });
|
|
await mountDocumentsListView({
|
|
context: {
|
|
allowed_company_ids: [1, 2],
|
|
},
|
|
});
|
|
expect("thead th[data-name='company_id']").toHaveCount(1);
|
|
});
|
|
|
|
test("company_id field visibility for portal in multicompany", async function () {
|
|
serverState.companies = [
|
|
{ id: 1, name: "Company 1", sequence: 1, parent_id: false, child_ids: [] },
|
|
{ id: 2, name: "Company 2", sequence: 2, parent_id: false, child_ids: [] },
|
|
];
|
|
const testUserGroups = ["base.group_portal", "base.group_multi_company"];
|
|
// We need to do this here and not on the model because has_group("base.group_user")
|
|
// is already in cache before the model method is called the first time.
|
|
patchWithCleanup(user, {
|
|
hasGroup: (group) => testUserGroups.includes(group),
|
|
});
|
|
const serverData = getDocumentsTestServerModelsData();
|
|
const currentUser = serverData["res.users"].find((u) => u.id === serverState.userId);
|
|
// Sync server data for consistency even if it is not really used.
|
|
Object.assign(currentUser, { group_ids: [], share: true });
|
|
await makeDocumentsMockEnv({ serverData });
|
|
await mountDocumentsListView({
|
|
context: {
|
|
allowed_company_ids: [1, 2],
|
|
},
|
|
});
|
|
expect("thead th[data-name='company_id']").toHaveCount(0);
|
|
});
|
|
|
|
test("file sharing via link with multiple subfolders", async function () {
|
|
let accessFolder1 = false;
|
|
let accessFolder2 = false;
|
|
let addFolder4 = false;
|
|
onRpc("/documents/touch/accessTokenFolder1", () => {
|
|
expect.step("touch 1");
|
|
accessFolder1 = true;
|
|
return { reload: true };
|
|
});
|
|
onRpc("/documents/touch/accessTokenFolder2", () => {
|
|
expect.step("touch 2");
|
|
accessFolder2 = true;
|
|
return { reload: true };
|
|
});
|
|
onRpc("/documents/touch/accessTokenFolder3", () => {
|
|
return { reload: true };
|
|
});
|
|
onRpc("/documents/touch/accessTokenFolder4", () => {
|
|
return { reload: true };
|
|
});
|
|
|
|
// Set active true/false to control the folders display
|
|
const folder2 = makeDocumentRecordData(2, "Folder 2", {
|
|
type: "folder",
|
|
is_folder: true,
|
|
folder_id: 1,
|
|
access_token: "accessTokenFolder2",
|
|
active: false,
|
|
});
|
|
const folder3 = makeDocumentRecordData(3, "Folder 3", {
|
|
type: "folder",
|
|
is_folder: true,
|
|
folder_id: 2,
|
|
access_token: "accessTokenFolder3",
|
|
active: false,
|
|
});
|
|
const folder4 = makeDocumentRecordData(4, "Folder 4", {
|
|
type: "folder",
|
|
is_folder: true,
|
|
folder_id: 2,
|
|
access_token: "accessTokenFolder4",
|
|
active: false,
|
|
});
|
|
const serverData = getDocumentsTestServerModelsData([folder2, folder3, folder4]);
|
|
|
|
const docEnv = await makeDocumentsMockEnv({ serverData });
|
|
const activateFolders = ({ args }) => {
|
|
folder2.active = accessFolder1;
|
|
folder3.active = accessFolder2;
|
|
if (addFolder4) {
|
|
folder4.active = accessFolder2;
|
|
// Ensure only one folder button available
|
|
folder3.active = false;
|
|
}
|
|
};
|
|
onRpc("search_panel_select_range", activateFolders);
|
|
onRpc("web_search_read", activateFolders);
|
|
|
|
const docService = docEnv.services["document.document"];
|
|
// Avoid logAccess 1000ms debounce timer
|
|
patchWithCleanup(docService, {
|
|
logAccess: (token) => docService._logAccess(token),
|
|
// Avoid duplicates due to focusRecord logAccess with no debounce
|
|
focusRecord: () => false,
|
|
});
|
|
await mountDocumentsListView();
|
|
|
|
await contains(`.o_search_panel_label[data-tooltip="Company"] .o_toggle_fold`).click();
|
|
expect(`.o_data_row .o_field_cell[name="name"]:contains("Folder 1")`).toHaveCount(1);
|
|
await contains(`.o_data_row .o_field_cell .o_field_documents_type_icon`).click();
|
|
expect(`.o_data_row .o_field_cell[name="name"]:contains("Folder 2")`).toHaveCount(1);
|
|
await contains(`.o_data_row .o_field_cell .o_field_documents_type_icon`).click();
|
|
expect(`.o_data_row .o_field_cell[name="name"]:contains("Folder 3")`).toHaveCount(1);
|
|
await contains(`.o_data_row .o_field_cell .o_field_documents_type_icon`).click();
|
|
|
|
expect.verifySteps(["touch 1", "touch 2"]);
|
|
|
|
expect(`.o_search_panel_label[data-tooltip="Folder 4"]`).toHaveCount(0);
|
|
// New sub-folder added without reloading
|
|
addFolder4 = true;
|
|
await contains(`.o_search_panel_label_title:contains("Folder 2")`).click();
|
|
expect(`.o_search_panel_label[data-tooltip="Folder 4"]`).toHaveCount(0);
|
|
expect(`.o_data_row .o_field_cell[name="name"]:contains("Folder 4")`).toHaveCount(1);
|
|
await contains(`.o_data_row .o_field_cell .o_field_documents_type_icon`).click();
|
|
expect(`.o_search_panel_label[data-tooltip="Folder 4"]`).toHaveCount(1);
|
|
expect.verifySteps(["touch 2"]);
|
|
});
|
|
|
|
test("edit mode is preserved when interacting with modals or control panel", async function () {
|
|
onRpc("/documents/touch/<access_token>", () => ({}));
|
|
const serverData = getDocumentsTestServerModelsData();
|
|
await makeDocumentsMockEnv({ serverData });
|
|
await mountDocumentsListView();
|
|
|
|
// Enter edit mode and open "Search More" modal
|
|
await contains(".o_data_row:nth-child(1) td.o_list_record_selector").click();
|
|
expect(".o_data_row_selected").toHaveCount(1);
|
|
await contains('.o_data_row:nth-child(1) td.o_list_many2one[name="partner_id"]').click();
|
|
await selectFieldDropdownItem("partner_id", "Search more...");
|
|
|
|
// Clicks inside the modal do not exit edit mode
|
|
await contains(`.modal-title:contains("Search: Related partner")`).click();
|
|
await animationFrame();
|
|
expect(".modal-title").toHaveText("Search: Related partner");
|
|
expect(".o_data_row_selected").toHaveCount(1);
|
|
await contains(".modal-footer .btn-secondary").click();
|
|
|
|
// Clicks near searchbar exit edit mode
|
|
await contains(".o_control_panel_breadcrumbs").click();
|
|
expect(".o_list_button_discard").toHaveCount(0);
|
|
expect(".o_data_row_selected").toHaveCount(1);
|
|
|
|
// Click below the list panel unselects the row
|
|
await contains(".o_renderer_with_searchpanel").click();
|
|
expect(".o_data_row_selected").toHaveCount(0);
|
|
});
|