mirror of
https://github.com/bringout/oca-ocb-report.git
synced 2026-04-22 10:22:07 +02:00
19.0 vanilla
This commit is contained in:
parent
62d197ac8b
commit
184bb0e321
667 changed files with 691406 additions and 239886 deletions
|
|
@ -0,0 +1,734 @@
|
|||
import { describe, expect, test } from "@odoo/hoot";
|
||||
import { queryAll, press, queryAllTexts } from "@odoo/hoot-dom";
|
||||
import { animationFrame } from "@odoo/hoot-mock";
|
||||
import { getBasicData, Product } from "@spreadsheet/../tests/helpers/data";
|
||||
import { createSpreadsheetDashboard } from "@spreadsheet_dashboard/../tests/helpers/dashboard_action";
|
||||
import {
|
||||
defineSpreadsheetDashboardModels,
|
||||
getDashboardServerData,
|
||||
} from "@spreadsheet_dashboard/../tests/helpers/data";
|
||||
import { contains, onRpc, patchWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { RPCError } from "@web/core/network/rpc";
|
||||
import { Deferred } from "@web/core/utils/concurrency";
|
||||
import { range } from "@web/core/utils/numbers";
|
||||
import { THIS_YEAR_GLOBAL_FILTER } from "@spreadsheet/../tests/helpers/global_filter";
|
||||
|
||||
describe.current.tags("desktop");
|
||||
defineSpreadsheetDashboardModels();
|
||||
|
||||
function getServerData(spreadsheetData) {
|
||||
const serverData = getDashboardServerData();
|
||||
serverData.models = {
|
||||
...serverData.models,
|
||||
...getBasicData(),
|
||||
};
|
||||
serverData.models["spreadsheet.dashboard.group"].records = [
|
||||
{
|
||||
published_dashboard_ids: [789],
|
||||
id: 1,
|
||||
name: "Pivot",
|
||||
},
|
||||
];
|
||||
serverData.models["spreadsheet.dashboard"].records = [
|
||||
{
|
||||
id: 789,
|
||||
name: "Spreadsheet with Pivot",
|
||||
json_data: JSON.stringify(spreadsheetData),
|
||||
spreadsheet_data: JSON.stringify(spreadsheetData),
|
||||
dashboard_group_id: 1,
|
||||
},
|
||||
];
|
||||
return serverData;
|
||||
}
|
||||
|
||||
test("display available spreadsheets", async () => {
|
||||
await createSpreadsheetDashboard();
|
||||
expect(".o_search_panel section").toHaveCount(2);
|
||||
expect(".o_search_panel li").toHaveCount(3);
|
||||
});
|
||||
|
||||
test("display the active spreadsheet", async () => {
|
||||
await createSpreadsheetDashboard();
|
||||
expect(".o_search_panel li.active").toHaveCount(1, {
|
||||
message: "It should have one active element",
|
||||
});
|
||||
expect(".o-spreadsheet").toHaveCount(1, { message: "It should display the spreadsheet" });
|
||||
});
|
||||
|
||||
test("Fold/unfold the search panel", async function () {
|
||||
await createSpreadsheetDashboard();
|
||||
await contains(".o_spreadsheet_dashboard_search_panel button").click();
|
||||
expect(".o_spreadsheet_dashboard_search_panel").toHaveCount(0);
|
||||
expect(".o_search_panel_sidebar").toHaveText("Container 1 / Dashboard CRM 1");
|
||||
await contains(".o_search_panel_sidebar button").click();
|
||||
expect(".o_search_panel_sidebar").toHaveCount(0);
|
||||
expect(".o_spreadsheet_dashboard_search_panel").toHaveCount(1);
|
||||
});
|
||||
|
||||
test("Folding dashboard from 'FAVORITES' group shows correct active dashboard group", async function () {
|
||||
await createSpreadsheetDashboard({
|
||||
mockRPC: async function (route, args) {
|
||||
if (
|
||||
args.method === "action_toggle_favorite" &&
|
||||
args.model === "spreadsheet.dashboard"
|
||||
) {
|
||||
expect.step("action_toggle_favorite");
|
||||
return true;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await contains(".o_dashboard_star").click();
|
||||
expect(".o_search_panel_section").toHaveCount(3);
|
||||
expect(".o_search_panel_category header b:first").toHaveText("FAVORITES");
|
||||
expect.verifySteps(["action_toggle_favorite"]);
|
||||
|
||||
await contains(".o_spreadsheet_dashboard_search_panel button").click();
|
||||
expect(".o_spreadsheet_dashboard_search_panel").toHaveCount(0);
|
||||
expect(".o_search_panel_sidebar").toHaveText("Container 1 / Dashboard CRM 1");
|
||||
});
|
||||
|
||||
test("Fold button invisible in the search panel without any dashboard", async function () {
|
||||
const serverData = getDashboardServerData();
|
||||
serverData.models["spreadsheet.dashboard"].records = [];
|
||||
serverData.models["spreadsheet.dashboard.group"].records = [];
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
expect(".o_spreadsheet_dashboard_search_panel button").toHaveCount(0);
|
||||
});
|
||||
|
||||
test("load action with specific dashboard", async () => {
|
||||
await createSpreadsheetDashboard({ spreadsheetId: 3 });
|
||||
expect(".o_search_panel li.active").toHaveText("Dashboard Accounting 1");
|
||||
});
|
||||
|
||||
test("can switch spreadsheet", async () => {
|
||||
await createSpreadsheetDashboard();
|
||||
const spreadsheets = queryAll(".o_search_panel li");
|
||||
|
||||
expect(spreadsheets[0]).toHaveClass("active");
|
||||
expect(spreadsheets[1]).not.toHaveClass("active");
|
||||
expect(spreadsheets[2]).not.toHaveClass("active");
|
||||
|
||||
await contains(spreadsheets[1]).click();
|
||||
|
||||
expect(spreadsheets[0]).not.toHaveClass("active");
|
||||
expect(spreadsheets[1]).toHaveClass("active");
|
||||
expect(spreadsheets[2]).not.toHaveClass("active");
|
||||
});
|
||||
|
||||
test("display no dashboard message", async () => {
|
||||
await createSpreadsheetDashboard({
|
||||
mockRPC: function (route, { model, method, args }) {
|
||||
if (method === "web_search_read" && model === "spreadsheet.dashboard.group") {
|
||||
return {
|
||||
records: [],
|
||||
length: 0,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
expect(".o_search_panel li").toHaveCount(0, {
|
||||
message: "It should not display any spreadsheet",
|
||||
});
|
||||
expect(".dashboard-loading-status").toHaveText("No available dashboard", {
|
||||
message: "It should display no dashboard message",
|
||||
});
|
||||
});
|
||||
|
||||
test("display error message", async () => {
|
||||
expect.errors(1);
|
||||
onRpc("/spreadsheet/dashboard/data/2", () => {
|
||||
const error = new RPCError();
|
||||
error.data = {};
|
||||
throw error;
|
||||
});
|
||||
await createSpreadsheetDashboard();
|
||||
expect(".o-spreadsheet").toHaveCount(1, { message: "It should display the spreadsheet" });
|
||||
await contains(".o_search_panel li:eq(1)").click();
|
||||
expect(".o_spreadsheet_dashboard_action .dashboard-loading-status.error").toHaveCount(1, {
|
||||
message: "It should display an error",
|
||||
});
|
||||
await contains(".o_search_panel li:eq(0)").click();
|
||||
expect(".o-spreadsheet").toHaveCount(1, { message: "It should display the spreadsheet" });
|
||||
expect(".o_renderer .error").toHaveCount(0, { message: "It should not display an error" });
|
||||
expect.verifyErrors(["RPC_ERROR"]);
|
||||
});
|
||||
|
||||
test("load dashboard that doesn't exist", async () => {
|
||||
expect.errors(1);
|
||||
await createSpreadsheetDashboard({
|
||||
spreadsheetId: 999,
|
||||
});
|
||||
expect(".o_spreadsheet_dashboard_action .dashboard-loading-status.error").toHaveCount(1, {
|
||||
message: "It should display an error",
|
||||
});
|
||||
expect.verifyErrors(["RPC_ERROR"]);
|
||||
});
|
||||
|
||||
test("Last selected spreadsheet is kept when go back from breadcrumb", async function () {
|
||||
const spreadsheetData = {
|
||||
version: 16,
|
||||
sheets: [
|
||||
{
|
||||
id: "sheet1",
|
||||
cells: { A1: { content: '=PIVOT.VALUE("1", "probability")' } },
|
||||
},
|
||||
],
|
||||
pivots: {
|
||||
1: {
|
||||
id: 1,
|
||||
colGroupBys: ["foo"],
|
||||
domain: [],
|
||||
measures: [{ field: "probability", operator: "avg" }],
|
||||
model: "partner",
|
||||
rowGroupBys: ["bar"],
|
||||
},
|
||||
},
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
serverData.models["spreadsheet.dashboard"].records.push({
|
||||
id: 790,
|
||||
name: "Second dashboard",
|
||||
json_data: JSON.stringify(spreadsheetData),
|
||||
spreadsheet_data: JSON.stringify(spreadsheetData),
|
||||
dashboard_group_id: 1,
|
||||
});
|
||||
serverData.models["spreadsheet.dashboard.group"].records[0].published_dashboard_ids.push(790);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
await contains(".o_search_panel li:last-child").click();
|
||||
await contains(".o-dashboard-clickable-cell").click();
|
||||
expect(".o_list_view").toHaveCount(1);
|
||||
await contains(".o_back_button").click();
|
||||
expect(".o_search_panel li:last-child").toHaveClass("active");
|
||||
});
|
||||
|
||||
test("Can clear filter date filter value that defaults to current period", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "date",
|
||||
label: "Period",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "date",
|
||||
label: "This Year",
|
||||
defaultValue: "this_year",
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
const year = luxon.DateTime.local().year;
|
||||
expect(".o_control_panel_actions .o_facet_value").toHaveText(String(year));
|
||||
await contains(".o_searchview_facet_label").click();
|
||||
await contains('.o-filter-item[data-id="2"] input').click();
|
||||
await contains(".o-dropdown-item[data-id='year'] .btn-previous").click();
|
||||
await contains(".o-filter-values-footer .btn-primary").click();
|
||||
|
||||
expect(".o_control_panel_actions .o_facet_value").toHaveText(String(year - 1));
|
||||
|
||||
expect(".o_control_panel_actions .o_facet_remove").toHaveCount(1);
|
||||
await contains(".o_control_panel_actions .o_facet_remove").click();
|
||||
|
||||
expect(".o_control_panel_actions .o_facet_remove").toHaveCount(0);
|
||||
});
|
||||
|
||||
test("share dashboard from dashboard view", async function () {
|
||||
patchWithCleanup(browser.navigator.clipboard, {
|
||||
writeText: (url) => {
|
||||
expect.step("share url copied");
|
||||
expect(url).toBe("localhost:8069/share/url/132465");
|
||||
},
|
||||
});
|
||||
const def = new Deferred();
|
||||
await createSpreadsheetDashboard({
|
||||
mockRPC: async function (route, args) {
|
||||
if (args.method === "action_get_share_url") {
|
||||
await def;
|
||||
expect.step("dashboard_shared");
|
||||
expect(args.model).toBe("spreadsheet.dashboard.share");
|
||||
return "localhost:8069/share/url/132465";
|
||||
}
|
||||
},
|
||||
});
|
||||
expect(".spreadsheet_share_dropdown").toHaveCount(0);
|
||||
await contains("i.fa-share-alt").click();
|
||||
await animationFrame();
|
||||
expect(".spreadsheet_share_dropdown .o_loading_state").toHaveText("Generating sharing link");
|
||||
def.resolve();
|
||||
await animationFrame();
|
||||
expect(".spreadsheet_share_dropdown .o_loading_state").toHaveCount(0);
|
||||
expect.verifySteps(["dashboard_shared", "share url copied"]);
|
||||
expect(".o_field_CopyClipboardChar").toHaveText("localhost:8069/share/url/132465");
|
||||
await contains(".fa-clipboard").click();
|
||||
expect.verifySteps(["share url copied"]);
|
||||
});
|
||||
|
||||
test("Changing filter values will create a new share", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "date",
|
||||
label: "Period",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "date",
|
||||
label: "This Year",
|
||||
defaultValue: "this_year",
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
let counter = 0;
|
||||
await createSpreadsheetDashboard({
|
||||
serverData,
|
||||
mockRPC: async function (route, args) {
|
||||
if (args.method === "action_get_share_url") {
|
||||
return `localhost:8069/share/url/${++counter}`;
|
||||
}
|
||||
},
|
||||
});
|
||||
await contains("i.fa-share-alt").click();
|
||||
await animationFrame();
|
||||
expect(".o_field_CopyClipboardChar").toHaveText(`localhost:8069/share/url/1`);
|
||||
|
||||
await contains("i.fa-share-alt").click(); // close share dropdown
|
||||
|
||||
await contains("i.fa-share-alt").click();
|
||||
await animationFrame();
|
||||
expect(".o_field_CopyClipboardChar").toHaveText(`localhost:8069/share/url/1`);
|
||||
|
||||
await contains("i.fa-share-alt").click();
|
||||
const year = luxon.DateTime.local().year;
|
||||
expect(".o_control_panel_actions .o_facet_value").toHaveText(String(year));
|
||||
await contains(".o_searchview_facet_label").click();
|
||||
await contains(".o-filter-value input").click();
|
||||
await contains(".o-dropdown-item[data-id='year'] .btn-previous").click();
|
||||
await contains(".o-filter-values-footer .btn-primary").click();
|
||||
|
||||
await contains("i.fa-share-alt").click();
|
||||
await animationFrame();
|
||||
expect(".o_field_CopyClipboardChar").toHaveText(`localhost:8069/share/url/2`);
|
||||
});
|
||||
|
||||
test("Should toggle favorite status of a dashboard when the 'Favorite' icon is clicked", async function () {
|
||||
onRpc("spreadsheet.dashboard", "action_toggle_favorite", ({ method }) => {
|
||||
expect.step(method);
|
||||
return true;
|
||||
});
|
||||
await createSpreadsheetDashboard();
|
||||
expect(".o_search_panel_section").toHaveCount(2);
|
||||
await contains(".o_dashboard_star").click();
|
||||
expect(".o_dashboard_star").toHaveClass("fa-star", {
|
||||
message: "The star should be filled",
|
||||
});
|
||||
expect(".o_search_panel_section").toHaveCount(3);
|
||||
expect.verifySteps(["action_toggle_favorite"]);
|
||||
expect(".o_search_panel_section.o_search_panel_category:first header b:first").toHaveText(
|
||||
"FAVORITES"
|
||||
);
|
||||
await contains(".o_dashboard_star").click();
|
||||
expect(".o_dashboard_star").not.toHaveClass("fa-star", {
|
||||
message: "The star should not be filled",
|
||||
});
|
||||
expect.verifySteps(["action_toggle_favorite"]);
|
||||
expect(".o_search_panel_section").toHaveCount(2);
|
||||
});
|
||||
|
||||
test("Global filter with same id is not shared between dashboards", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
serverData.models["spreadsheet.dashboard"].records.push({
|
||||
id: 790,
|
||||
name: "Spreadsheet dup. with Pivot",
|
||||
json_data: JSON.stringify(spreadsheetData),
|
||||
spreadsheet_data: JSON.stringify(spreadsheetData),
|
||||
dashboard_group_id: 1,
|
||||
});
|
||||
serverData.models["spreadsheet.dashboard.group"].records[0].published_dashboard_ids = [
|
||||
789, 790,
|
||||
];
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
expect(".o_searchview_facet").toHaveCount(0);
|
||||
await contains(".o_spreadsheet_dashboard_action .dropdown-toggle").click();
|
||||
|
||||
await contains(".o-autocomplete--input.o_input").click();
|
||||
expect(".o-filter-value .o_tag_badge_text").toHaveCount(0);
|
||||
await contains(".dropdown-item:first").click();
|
||||
expect(".o-filter-value .o_tag_badge_text").toHaveCount(1);
|
||||
|
||||
await contains(".o-filter-values-footer .btn-primary").click();
|
||||
expect(".o_searchview_facet").toHaveCount(1);
|
||||
|
||||
await contains(".o_search_panel li:last-child").click();
|
||||
expect(".o_searchview_facet").toHaveCount(0);
|
||||
});
|
||||
|
||||
test("Search bar is not present if there is no global filters", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
expect(".o_sp_dashboard_search").toHaveCount(0);
|
||||
});
|
||||
|
||||
test("Can add a new global filter from the search bar", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_spreadsheet_dashboard_action .dropdown-toggle").click();
|
||||
|
||||
expect(".o-autocomplete--input.o_input").toHaveCount(1);
|
||||
expect(".o-autocomplete--input.o_input").toHaveValue("");
|
||||
await contains(".o-autocomplete--input.o_input").click();
|
||||
await contains(".o-autocomplete--dropdown-item").click();
|
||||
await contains(".o-filter-values-footer .btn-primary").click();
|
||||
|
||||
expect(".o_searchview_facet").toHaveCount(1);
|
||||
expect(".o_searchview_facet .o_searchview_facet_label").toHaveText("Relation Filter");
|
||||
expect(".o_searchview_facet .o_facet_value").toHaveText("xphone");
|
||||
});
|
||||
|
||||
test("Can open the dialog by clicking on a facet", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
defaultValue: { operator: "in", ids: [37] }, // xphone
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
expect(".o_searchview_facet").toHaveCount(1);
|
||||
await contains(".o_searchview_facet .o_searchview_facet_label ").click();
|
||||
expect(".o-filter-values").toHaveCount(1);
|
||||
});
|
||||
|
||||
test("Can open the dialog by clicking on the search bar", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
defaultValue: { operator: "in", ids: [37] }, // xphone
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview input").click();
|
||||
expect(".o-filter-values").toHaveCount(1);
|
||||
});
|
||||
|
||||
test("Changes of global filters are not dispatched while inside the dialog", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
expect(model.getters.getGlobalFilterValue("1")).toBe(undefined);
|
||||
|
||||
await contains(".o_spreadsheet_dashboard_action .dropdown-toggle").click();
|
||||
|
||||
await contains(".o-autocomplete--input.o_input").click();
|
||||
await contains(".o-autocomplete--dropdown-item").click();
|
||||
expect(model.getters.getGlobalFilterValue("1")).toBe(undefined);
|
||||
await contains(".o-filter-values-footer .btn-primary").click();
|
||||
expect(model.getters.getGlobalFilterValue("1")).toEqual({ operator: "in", ids: [37] });
|
||||
});
|
||||
|
||||
test("First global filter date is displayed as button", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
defaultValue: { operator: "in", ids: [37] },
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "date",
|
||||
label: "Period",
|
||||
defaultValue: "this_year",
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
expect(".o_sp_date_filter_button").toHaveCount(1);
|
||||
expect(".o_searchview_facet").toHaveCount(1);
|
||||
});
|
||||
|
||||
test("No date buttons are displayed if there is no date filter", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
expect(".o_sp_date_filter_button").toHaveCount(0);
|
||||
});
|
||||
|
||||
test("Unknown value for relation filter is displayed as inaccessible", async function () {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
defaultValue: { operator: "in", ids: [9999] }, // unknown product
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
expect(".o_searchview_facet").toHaveCount(1);
|
||||
expect(".o_searchview_facet .o_facet_value").toHaveText("Inaccessible/missing record ID");
|
||||
});
|
||||
|
||||
describe("Quick search bar", () => {
|
||||
const productFilter = {
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Product",
|
||||
modelName: "product",
|
||||
};
|
||||
|
||||
const selectionFilter = {
|
||||
id: "55",
|
||||
type: "selection",
|
||||
label: "Selection Filter",
|
||||
resModel: "res.currency",
|
||||
selectionField: "position",
|
||||
};
|
||||
|
||||
test("Can quick search a string in a relational filter", async function () {
|
||||
const spreadsheetData = { globalFilters: [productFilter] };
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("phone");
|
||||
expect(".o-dropdown-item.focus").toHaveText("Search Product for: phone");
|
||||
await press("Enter");
|
||||
|
||||
const filterValue = model.getters.getGlobalFilterValue(productFilter.id);
|
||||
expect(filterValue).toEqual({ operator: "ilike", strings: ["phone"] });
|
||||
});
|
||||
|
||||
test("Can quick search a string in a relational filter if a record was already selected", async function () {
|
||||
const filter = { ...productFilter, defaultValue: { operator: "in", ids: [37] } };
|
||||
const spreadsheetData = { globalFilters: [filter] };
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("test");
|
||||
expect(".o-dropdown-item.focus").toHaveText("Search Product for: test");
|
||||
await press("Enter");
|
||||
|
||||
const filterValue = model.getters.getGlobalFilterValue(productFilter.id);
|
||||
expect(filterValue).toEqual({ operator: "ilike", strings: ["test"] });
|
||||
});
|
||||
|
||||
test("Can quick search a specific record in a relational filter", async function () {
|
||||
const spreadsheetData = { globalFilters: [productFilter] };
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("x");
|
||||
expect(".o-dropdown-item.focus").toHaveText("Search Product for: x");
|
||||
await contains(".o-dropdown-item.focus .o_expand").click();
|
||||
|
||||
const children = queryAll(".o-dropdown-item.o_indent");
|
||||
expect(children.map((el) => el.innerText)).toEqual(["xphone", "xpad"]);
|
||||
await contains(children[0]).click();
|
||||
|
||||
const filterValue = model.getters.getGlobalFilterValue(productFilter.id);
|
||||
expect(filterValue).toEqual({ operator: "in", ids: [37] });
|
||||
});
|
||||
|
||||
test("Can load more records in the quick search", async function () {
|
||||
for (let i = 0; i < 15; i++) {
|
||||
Product._records.push({ id: i, display_name: "name" + i });
|
||||
}
|
||||
const serverData = getServerData({ globalFilters: [productFilter] });
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("name");
|
||||
expect(".o-dropdown-item.focus").toHaveText("Search Product for: name");
|
||||
await contains(".o-dropdown-item.focus .o_expand").click();
|
||||
|
||||
const children = queryAll(".o-dropdown-item.o_indent");
|
||||
expect(children.map((el) => el.innerText)).toEqual([
|
||||
...range(0, 9).map((i) => "name" + i),
|
||||
"Load more",
|
||||
]);
|
||||
await contains(children.at(-1)).click();
|
||||
|
||||
expect(queryAllTexts(".o-dropdown-item.o_indent")).toEqual(
|
||||
range(0, 15).map((i) => "name" + i)
|
||||
);
|
||||
});
|
||||
|
||||
test("Can quick search a string in a text filter", async function () {
|
||||
const spreadsheetData = { globalFilters: [{ id: "2", type: "text", label: "Text" }] };
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("phone");
|
||||
expect(".o-dropdown-item.focus").toHaveText("Search Text for: phone");
|
||||
await press("Enter");
|
||||
|
||||
const filterValue = model.getters.getGlobalFilterValue("2");
|
||||
expect(filterValue).toEqual({ operator: "ilike", strings: ["phone"] });
|
||||
});
|
||||
|
||||
test("Can quick search a string in a text filter with a range of allowed values", async function () {
|
||||
const spreadsheetData = {
|
||||
sheets: [{ id: "sh1", name: "Sh1", cells: { A1: "phone", A2: "tablet", A3: "table" } }],
|
||||
globalFilters: [
|
||||
{
|
||||
id: "2",
|
||||
type: "text",
|
||||
label: "Text",
|
||||
rangesOfAllowedValues: ["Sh1!A1:A5"],
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("a");
|
||||
expect(".o-dropdown-item.focus").toHaveText("Search Text for: a");
|
||||
await press("ArrowRight");
|
||||
await animationFrame();
|
||||
|
||||
const children = queryAll(".o-dropdown-item.o_indent");
|
||||
expect(children.map((el) => el.innerText)).toEqual(["tablet", "table"]);
|
||||
await contains(children[1]).click();
|
||||
|
||||
const filterValue = model.getters.getGlobalFilterValue("2");
|
||||
expect(filterValue).toEqual({ operator: "ilike", strings: ["table"] });
|
||||
});
|
||||
|
||||
test("Cannot search for a string that is not in rangesOfAllowedValues", async function () {
|
||||
const spreadsheetData = {
|
||||
sheets: [{ id: "sh1", name: "Sh1", cells: { A1: "phone", A2: "tablet", A3: "table" } }],
|
||||
globalFilters: [
|
||||
{
|
||||
id: "2",
|
||||
type: "text",
|
||||
label: "Text",
|
||||
rangesOfAllowedValues: ["Sh1!A1:A5"],
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("desk");
|
||||
expect(".o-dropdown-item.focus").toHaveText("Search Text for: desk");
|
||||
await press("Enter");
|
||||
|
||||
const filterValue = model.getters.getGlobalFilterValue("2");
|
||||
expect(filterValue).toEqual(undefined);
|
||||
});
|
||||
|
||||
test("Can quick search a selection filter value", async function () {
|
||||
const spreadsheetData = { globalFilters: [selectionFilter] };
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("a");
|
||||
expect(".o-dropdown-item.focus").toHaveText("Search Selection Filter for: a");
|
||||
await contains(".o-dropdown-item.focus .o_expand").click();
|
||||
|
||||
const children = queryAll(".o-dropdown-item.o_indent");
|
||||
expect(children.map((el) => el.innerText)).toEqual(["A"]);
|
||||
await contains(children[0]).click();
|
||||
|
||||
const filterValue = model.getters.getGlobalFilterValue(selectionFilter.id);
|
||||
expect(filterValue).toEqual({ operator: "in", selectionValues: ["after"] });
|
||||
});
|
||||
|
||||
test("Date and numeric filters are not in the quick search results", async function () {
|
||||
const numericFilter = { id: "255", type: "numeric", label: "Numeric Filter" };
|
||||
const spreadsheetData = {
|
||||
globalFilters: [productFilter, THIS_YEAR_GLOBAL_FILTER, numericFilter, selectionFilter],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_searchview_input").edit("phone");
|
||||
expect(queryAllTexts(".o-dropdown-item")).toEqual([
|
||||
"Search Product for: phone",
|
||||
"Search Selection Filter for: phone",
|
||||
]);
|
||||
});
|
||||
|
||||
test("Pressing backspace will remove the last facet", async function () {
|
||||
const filter = { ...productFilter, defaultValue: { operator: "in", ids: [37] } };
|
||||
const spreadsheetData = { globalFilters: [filter] };
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const { model } = await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
let filterValue = model.getters.getGlobalFilterValue(productFilter.id);
|
||||
expect(filterValue).toEqual({ operator: "in", ids: [37] });
|
||||
|
||||
await contains(".o_searchview_input").focus();
|
||||
await press("Backspace");
|
||||
|
||||
filterValue = model.getters.getGlobalFilterValue(productFilter.id);
|
||||
expect(filterValue).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import { describe, expect, test, getFixture } from "@odoo/hoot";
|
||||
import { getBasicData } from "@spreadsheet/../tests/helpers/data";
|
||||
import { createSpreadsheetDashboard } from "@spreadsheet_dashboard/../tests/helpers/dashboard_action";
|
||||
import {
|
||||
defineSpreadsheetDashboardModels,
|
||||
getDashboardServerData,
|
||||
} from "@spreadsheet_dashboard/../tests/helpers/data";
|
||||
import { contains } from "@web/../tests/web_test_helpers";
|
||||
|
||||
describe.current.tags("mobile");
|
||||
defineSpreadsheetDashboardModels();
|
||||
|
||||
function getServerData(spreadsheetData) {
|
||||
const serverData = getDashboardServerData();
|
||||
serverData.models = {
|
||||
...serverData.models,
|
||||
...getBasicData(),
|
||||
};
|
||||
serverData.models["spreadsheet.dashboard.group"].records = [
|
||||
{
|
||||
published_dashboard_ids: [789],
|
||||
id: 1,
|
||||
name: "Pivot",
|
||||
},
|
||||
];
|
||||
serverData.models["spreadsheet.dashboard"].records = [
|
||||
{
|
||||
id: 789,
|
||||
name: "Spreadsheet with Pivot",
|
||||
json_data: JSON.stringify(spreadsheetData),
|
||||
spreadsheet_data: JSON.stringify(spreadsheetData),
|
||||
dashboard_group_id: 1,
|
||||
},
|
||||
];
|
||||
return serverData;
|
||||
}
|
||||
|
||||
test("Search input can be toggled", async () => {
|
||||
const productFilter = { id: "1", type: "relation", label: "Product", modelName: "product" };
|
||||
const spreadsheetData = { globalFilters: [productFilter] };
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
expect(".o_searchview_input").toHaveCount(0);
|
||||
|
||||
await contains(".o_search_toggler button").click();
|
||||
expect(".o_searchview_input").toHaveCount(1);
|
||||
});
|
||||
|
||||
test("Search input is not focusable in mobile", async () => {
|
||||
const productFilter = {
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Product",
|
||||
modelName: "product",
|
||||
};
|
||||
const spreadsheetData = { globalFilters: [productFilter] };
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
|
||||
await contains(".o_search_toggler button").click();
|
||||
await contains(".o_searchview_input").click();
|
||||
|
||||
const input = getFixture().querySelector(".o_searchview_input");
|
||||
expect(document.activeElement).not.toBe(input);
|
||||
expect(".o_bottom_sheet .o-filter-values").toHaveCount(1);
|
||||
});
|
||||
|
|
@ -1,254 +0,0 @@
|
|||
/** @odoo-module */
|
||||
|
||||
import {
|
||||
getFixture,
|
||||
click,
|
||||
legacyExtraNextTick,
|
||||
nextTick,
|
||||
editInput,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { getDashboardServerData } from "../utils/data";
|
||||
import { getBasicData, getBasicListArchs } from "@spreadsheet/../tests/utils/data";
|
||||
import { createSpreadsheetDashboard } from "../utils/dashboard_action";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { errorService } from "@web/core/errors/error_service";
|
||||
import { RPCError } from "@web/core/network/rpc_service";
|
||||
|
||||
QUnit.module("spreadsheet_dashboard > Dashboard > Dashboard action");
|
||||
|
||||
function getServerData(spreadsheetData) {
|
||||
const serverData = getDashboardServerData();
|
||||
serverData.models = {
|
||||
...serverData.models,
|
||||
...getBasicData(),
|
||||
};
|
||||
serverData.views = getBasicListArchs();
|
||||
serverData.models["spreadsheet.dashboard.group"].records = [
|
||||
{
|
||||
dashboard_ids: [789],
|
||||
id: 1,
|
||||
name: "Pivot",
|
||||
},
|
||||
];
|
||||
serverData.models["spreadsheet.dashboard"].records = [
|
||||
{
|
||||
id: 789,
|
||||
name: "Spreadsheet with Pivot",
|
||||
json_data: JSON.stringify(spreadsheetData),
|
||||
raw: JSON.stringify(spreadsheetData),
|
||||
dashboard_group_id: 1,
|
||||
},
|
||||
];
|
||||
return serverData;
|
||||
}
|
||||
|
||||
QUnit.test("display available spreadsheets", async (assert) => {
|
||||
await createSpreadsheetDashboard();
|
||||
assert.containsN(getFixture(), ".o_search_panel section", 2);
|
||||
assert.containsN(getFixture(), ".o_search_panel li", 3);
|
||||
});
|
||||
|
||||
QUnit.test("display the active spreadsheet", async (assert) => {
|
||||
await createSpreadsheetDashboard();
|
||||
assert.containsOnce(
|
||||
getFixture(),
|
||||
".o_search_panel li.active",
|
||||
"It should have one active element"
|
||||
);
|
||||
assert.containsOnce(getFixture(), ".o-spreadsheet", "It should display the spreadsheet");
|
||||
});
|
||||
|
||||
QUnit.test("load action with specific dashboard", async (assert) => {
|
||||
await createSpreadsheetDashboard({ spreadsheetId: 3 });
|
||||
const active = getFixture().querySelector(".o_search_panel li.active");
|
||||
assert.strictEqual(active.innerText, "Dashboard Accounting 1");
|
||||
});
|
||||
|
||||
QUnit.test("can switch spreadsheet", async (assert) => {
|
||||
await createSpreadsheetDashboard();
|
||||
const fixture = getFixture();
|
||||
const spreadsheets = fixture.querySelectorAll(".o_search_panel li");
|
||||
assert.ok(spreadsheets[0].className.includes("active"));
|
||||
assert.notOk(spreadsheets[1].className.includes("active"));
|
||||
assert.notOk(spreadsheets[2].className.includes("active"));
|
||||
await click(spreadsheets[1]);
|
||||
assert.notOk(spreadsheets[0].className.includes("active"));
|
||||
assert.ok(spreadsheets[1].className.includes("active"));
|
||||
assert.notOk(spreadsheets[2].className.includes("active"));
|
||||
});
|
||||
|
||||
QUnit.test("display no dashboard message", async (assert) => {
|
||||
await createSpreadsheetDashboard({
|
||||
mockRPC: function (route, { model, method, args }) {
|
||||
if (method === "search_read" && model === "spreadsheet.dashboard.group") {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
});
|
||||
const fixture = getFixture();
|
||||
assert.containsNone(fixture, ".o_search_panel li", "It should not display any spreadsheet");
|
||||
assert.strictEqual(
|
||||
fixture.querySelector(".dashboard-loading-status").innerText,
|
||||
"No available dashboard",
|
||||
"It should display no dashboard message"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("display error message", async (assert) => {
|
||||
registry.category("services").add("error", errorService);
|
||||
await createSpreadsheetDashboard({
|
||||
mockRPC: function (route, args) {
|
||||
if (
|
||||
args.model === "spreadsheet.dashboard" &&
|
||||
((args.method === "read" && args.args[0][0] === 2 && args.args[1][0] === "raw") ||
|
||||
// this is not correct from a module dependency POV but it's required for the test
|
||||
// to pass when `spreadsheet_dashboard_edition` module is installed
|
||||
(args.method === "join_spreadsheet_session" && args.args[0] === 2))
|
||||
) {
|
||||
const error = new RPCError();
|
||||
error.data = {};
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
const fixture = getFixture();
|
||||
const spreadsheets = fixture.querySelectorAll(".o_search_panel li");
|
||||
assert.containsOnce(fixture, ".o-spreadsheet", "It should display the spreadsheet");
|
||||
await click(spreadsheets[1]);
|
||||
assert.containsOnce(
|
||||
fixture,
|
||||
".o_spreadsheet_dashboard_action .dashboard-loading-status.error",
|
||||
"It should display an error"
|
||||
);
|
||||
await click(spreadsheets[0]);
|
||||
assert.containsOnce(fixture, ".o-spreadsheet", "It should display the spreadsheet");
|
||||
assert.containsNone(fixture, ".o_renderer .error", "It should not display an error");
|
||||
});
|
||||
|
||||
QUnit.test("load dashboard that doesn't exist", async (assert) => {
|
||||
registry.category("services").add("error", errorService);
|
||||
await createSpreadsheetDashboard({
|
||||
spreadsheetId: 999,
|
||||
});
|
||||
const fixture = getFixture();
|
||||
assert.containsOnce(
|
||||
fixture,
|
||||
".o_spreadsheet_dashboard_action .dashboard-loading-status.error",
|
||||
"It should display an error"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"Last selected spreadsheet is kept when go back from breadcrumb",
|
||||
async function (assert) {
|
||||
const spreadsheetData = {
|
||||
sheets: [
|
||||
{
|
||||
id: "sheet1",
|
||||
cells: { A1: { content: `=PIVOT("1", "probability")` } },
|
||||
},
|
||||
],
|
||||
pivots: {
|
||||
1: {
|
||||
id: 1,
|
||||
colGroupBys: ["foo"],
|
||||
domain: [],
|
||||
measures: [{ field: "probability", operator: "avg" }],
|
||||
model: "partner",
|
||||
rowGroupBys: ["bar"],
|
||||
},
|
||||
},
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const fixture = getFixture();
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
await click(fixture, ".o_search_panel li:last-child");
|
||||
await click(fixture, ".o-dashboard-clickable-cell");
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(fixture, ".o_list_view");
|
||||
await click(document.body.querySelector(".o_back_button"));
|
||||
await legacyExtraNextTick();
|
||||
assert.hasClass(fixture.querySelector(".o_search_panel li:last-child"), "active");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"Can clear filter date filter value that defaults to current period",
|
||||
async function (assert) {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "date",
|
||||
label: "Date Filter",
|
||||
rangeType: "year",
|
||||
defaultValue: {},
|
||||
defaultsToCurrentPeriod: true,
|
||||
pivotFields: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
const fixture = getFixture();
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
const year = fixture.querySelector(".o_cp_top_right input.o_datepicker_input");
|
||||
const this_year = luxon.DateTime.local().year;
|
||||
assert.equal(year.value, String(this_year));
|
||||
const input = fixture.querySelector(
|
||||
"input.o_datepicker_input.o_input.datetimepicker-input"
|
||||
);
|
||||
await click(input);
|
||||
await editInput(input, null, String(this_year - 1));
|
||||
await nextTick();
|
||||
|
||||
assert.equal(year.value, String(this_year - 1));
|
||||
assert.containsOnce(fixture, ".o_cp_top_right .fa-times");
|
||||
await click(fixture.querySelector(".o_cp_top_right .fa-times"));
|
||||
|
||||
assert.containsNone(fixture, ".o_cp_top_right .fa-times");
|
||||
assert.equal(year.value, "");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("Global filter with same id is not shared between dashboards", async function (assert) {
|
||||
const spreadsheetData = {
|
||||
globalFilters: [
|
||||
{
|
||||
id: "1",
|
||||
type: "relation",
|
||||
label: "Relation Filter",
|
||||
modelName: "product",
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getServerData(spreadsheetData);
|
||||
serverData.models["spreadsheet.dashboard"].records.push({
|
||||
id: 790,
|
||||
name: "Spreadsheet dup. with Pivot",
|
||||
json_data: JSON.stringify(spreadsheetData),
|
||||
raw: JSON.stringify(spreadsheetData),
|
||||
dashboard_group_id: 1,
|
||||
});
|
||||
serverData.models["spreadsheet.dashboard.group"].records[0].dashboard_ids = [789, 790];
|
||||
const fixture = getFixture();
|
||||
await createSpreadsheetDashboard({ serverData });
|
||||
assert.containsNone(
|
||||
fixture,
|
||||
".o-filter-value .o_tag_badge_text",
|
||||
"It should not display any filter value"
|
||||
);
|
||||
await click(fixture.querySelector(".o-autocomplete--input.o_input"));
|
||||
await click(fixture.querySelector(".dropdown-item"));
|
||||
assert.containsN(
|
||||
fixture,
|
||||
".o-filter-value .o_tag_badge_text",
|
||||
1,
|
||||
"It should not display any filter value"
|
||||
);
|
||||
await click(fixture.querySelector(".o_search_panel li:last-child"));
|
||||
assert.containsNone(
|
||||
fixture,
|
||||
".o-filter-value .o_tag_badge_text",
|
||||
"It should not display any filter value"
|
||||
);
|
||||
});
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import { describe, expect, test } from "@odoo/hoot";
|
||||
import { makeMockEnv, contains, mountWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { defineSpreadsheetModels } from "@spreadsheet/../tests/helpers/data";
|
||||
import { DashboardDateFilter } from "@spreadsheet_dashboard/bundle/dashboard_action/dashboard_date_filter/dashboard_date_filter";
|
||||
|
||||
describe.current.tags("desktop");
|
||||
defineSpreadsheetModels();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {{ model: Model, filter: object}} props
|
||||
*/
|
||||
async function mountDashboardFilterValue(env, props) {
|
||||
await mountWithCleanup(DashboardDateFilter, { props, env });
|
||||
}
|
||||
|
||||
test("Can display the input as a button", async function () {
|
||||
const env = await makeMockEnv();
|
||||
await mountDashboardFilterValue(env, {
|
||||
value: { type: "range", from: "2023-01-01", to: "2023-01-31" },
|
||||
update: () => {},
|
||||
});
|
||||
expect("button").toHaveCount(3);
|
||||
expect(".o-date-filter-value").toHaveText("January 1 – 31, 2023");
|
||||
});
|
||||
|
||||
test("Can navigate with buttons to select the next period", async function () {
|
||||
const env = await makeMockEnv();
|
||||
await mountDashboardFilterValue(env, {
|
||||
value: { type: "month", month: 1, year: 2023 },
|
||||
update: (value) => {
|
||||
expect.step("update");
|
||||
expect(value).toEqual({ type: "month", month: 2, year: 2023 });
|
||||
},
|
||||
});
|
||||
await contains(".btn-next-date").click();
|
||||
expect.verifySteps(["update"]);
|
||||
});
|
||||
|
||||
test("Can navigate with buttons to select the previous period", async function () {
|
||||
const env = await makeMockEnv();
|
||||
await mountDashboardFilterValue(env, {
|
||||
value: { type: "month", month: 1, year: 2023 },
|
||||
update: (value) => {
|
||||
expect.step("update");
|
||||
expect(value).toEqual({ type: "month", month: 12, year: 2022 });
|
||||
},
|
||||
});
|
||||
await contains(".btn-previous-date").click();
|
||||
expect.verifySteps(["update"]);
|
||||
});
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
import { expect, test } from "@odoo/hoot";
|
||||
import { animationFrame } from "@odoo/hoot-mock";
|
||||
import { getCellValue } from "@spreadsheet/../tests/helpers/getters";
|
||||
import { makeSpreadsheetMockEnv } from "@spreadsheet/../tests/helpers/model";
|
||||
import { waitForDataLoaded } from "@spreadsheet/helpers/model";
|
||||
import {
|
||||
defineSpreadsheetDashboardModels,
|
||||
getDashboardServerData,
|
||||
} from "@spreadsheet_dashboard/../tests/helpers/data";
|
||||
import {
|
||||
DashboardLoader,
|
||||
Status,
|
||||
} from "@spreadsheet_dashboard/bundle/dashboard_action/dashboard_loader_service";
|
||||
import { onRpc, patchWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
import { RPCError } from "@web/core/network/rpc";
|
||||
|
||||
defineSpreadsheetDashboardModels();
|
||||
|
||||
/**
|
||||
* @param {object} [params]
|
||||
* @param {object} [params.serverData]
|
||||
* @param {function} [params.mockRPC]
|
||||
* @returns {Promise<DashboardLoader>}
|
||||
*/
|
||||
async function createDashboardLoader(params = {}) {
|
||||
const env = await makeSpreadsheetMockEnv({
|
||||
serverData: params.serverData || getDashboardServerData(),
|
||||
mockRPC: params.mockRPC,
|
||||
});
|
||||
return new DashboardLoader(env, env.services.orm, async (dashboardId) => {
|
||||
const [record] = await env.services.orm.read(
|
||||
"spreadsheet.dashboard",
|
||||
[dashboardId],
|
||||
["spreadsheet_data"]
|
||||
);
|
||||
return { data: JSON.parse(record.spreadsheet_data), revisions: [] };
|
||||
});
|
||||
}
|
||||
|
||||
test("load all dashboards of all containers", async () => {
|
||||
const loader = await createDashboardLoader();
|
||||
loader.load();
|
||||
expect(loader.getDashboardGroups()).toEqual([]);
|
||||
await animationFrame();
|
||||
expect(loader.getDashboardGroups()).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
name: "Container 1",
|
||||
dashboards: [
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
name: "Dashboard CRM 1",
|
||||
is_favorite: false,
|
||||
},
|
||||
status: Status.NotLoaded,
|
||||
},
|
||||
{
|
||||
data: {
|
||||
id: 2,
|
||||
name: "Dashboard CRM 2",
|
||||
is_favorite: false,
|
||||
},
|
||||
status: Status.NotLoaded,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Container 2",
|
||||
dashboards: [
|
||||
{
|
||||
data: {
|
||||
id: 3,
|
||||
name: "Dashboard Accounting 1",
|
||||
is_favorite: false,
|
||||
},
|
||||
status: Status.NotLoaded,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("load twice does not duplicate spreadsheets", async () => {
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
expect(loader.getDashboardGroups()[1].dashboards).toMatchObject([{ status: Status.NotLoaded }]);
|
||||
await loader.load();
|
||||
expect(loader.getDashboardGroups()[1].dashboards).toMatchObject([{ status: Status.NotLoaded }]);
|
||||
});
|
||||
|
||||
test("load spreadsheet data", async () => {
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
const result = loader.getDashboard(3);
|
||||
expect(result.status).toBe(Status.Loading);
|
||||
await animationFrame();
|
||||
expect(result.status).toBe(Status.Loaded);
|
||||
expect(result.model).not.toBe(undefined);
|
||||
});
|
||||
|
||||
test("load spreadsheet data only once", async () => {
|
||||
onRpc("/spreadsheet/dashboard/data/3", () => expect.step("spreadsheet 3 loaded"));
|
||||
const loader = await createDashboardLoader({
|
||||
mockRPC: function (route, args) {
|
||||
if (args.model === "spreadsheet.dashboard" && args.method === "read") {
|
||||
// read names
|
||||
expect.step(`spreadsheet ${args.args[0]} loaded`);
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
let result = loader.getDashboard(3);
|
||||
await animationFrame();
|
||||
expect(result.status).toBe(Status.Loaded);
|
||||
expect.verifySteps(["spreadsheet 3 loaded"]);
|
||||
result = loader.getDashboard(3);
|
||||
await animationFrame();
|
||||
expect(result.status).toBe(Status.Loaded);
|
||||
expect.verifySteps([]);
|
||||
});
|
||||
|
||||
test("don't return empty dashboard group", async () => {
|
||||
const loader = await createDashboardLoader({
|
||||
mockRPC: async function (route, args) {
|
||||
if (args.method === "web_search_read" && args.model === "spreadsheet.dashboard.group") {
|
||||
return {
|
||||
length: 2,
|
||||
records: [
|
||||
{
|
||||
id: 45,
|
||||
name: "Group A",
|
||||
published_dashboard_ids: [{ id: 1, name: "Dashboard CRM 1" }],
|
||||
},
|
||||
{
|
||||
id: 46,
|
||||
name: "Group B",
|
||||
published_dashboard_ids: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
expect(loader.getDashboardGroups()).toEqual([
|
||||
{
|
||||
id: 45,
|
||||
name: "Group A",
|
||||
dashboards: [
|
||||
{
|
||||
data: { id: 1, name: "Dashboard CRM 1" },
|
||||
status: Status.NotLoaded,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("load multiple spreadsheets", async () => {
|
||||
onRpc("/spreadsheet/dashboard/data/1", () => expect.step("spreadsheet 1 loaded"));
|
||||
onRpc("/spreadsheet/dashboard/data/2", () => expect.step("spreadsheet 2 loaded"));
|
||||
const loader = await createDashboardLoader({
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "web_search_read" && args.model === "spreadsheet.dashboard.group") {
|
||||
expect.step("load groups");
|
||||
}
|
||||
if (args.method === "read" && args.model === "spreadsheet.dashboard") {
|
||||
// read names
|
||||
expect.step(`spreadsheet ${args.args[0]} loaded`);
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
expect.verifySteps(["load groups"]);
|
||||
loader.getDashboard(1);
|
||||
await animationFrame();
|
||||
expect.verifySteps(["spreadsheet 1 loaded"]);
|
||||
loader.getDashboard(2);
|
||||
await animationFrame();
|
||||
expect.verifySteps(["spreadsheet 2 loaded"]);
|
||||
loader.getDashboard(1);
|
||||
await animationFrame();
|
||||
expect.verifySteps([]);
|
||||
});
|
||||
|
||||
test("load spreadsheet data with error", async () => {
|
||||
onRpc("/spreadsheet/dashboard/data/*", () => {
|
||||
const error = new RPCError();
|
||||
error.data = { message: "Bip" };
|
||||
throw error;
|
||||
});
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
const result = loader.getDashboard(3);
|
||||
expect(result.status).toBe(Status.Loading);
|
||||
await result.promise.catch(() => expect.step("error"));
|
||||
expect(result.status).toBe(Status.Error);
|
||||
expect(result.error.data.message).toBe("Bip");
|
||||
// error is thrown
|
||||
expect.verifySteps(["error"]);
|
||||
});
|
||||
|
||||
test("async formulas are correctly evaluated", async () => {
|
||||
const spreadsheetData = {
|
||||
sheets: [
|
||||
{
|
||||
id: "sheet1",
|
||||
cells: {
|
||||
A1: '=ODOO.CURRENCY.RATE("EUR","USD")', // an async formula
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getDashboardServerData();
|
||||
const dashboardId = 15;
|
||||
serverData.models["spreadsheet.dashboard.group"].records = [
|
||||
{ id: 1, name: "Container 1", published_dashboard_ids: [dashboardId] },
|
||||
];
|
||||
serverData.models["spreadsheet.dashboard"].records = [
|
||||
{
|
||||
id: dashboardId,
|
||||
spreadsheet_data: JSON.stringify(spreadsheetData),
|
||||
json_data: JSON.stringify(spreadsheetData),
|
||||
name: "Dashboard Accounting 1",
|
||||
dashboard_group_id: 1,
|
||||
},
|
||||
];
|
||||
const loader = await createDashboardLoader({
|
||||
serverData,
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "get_rates_for_spreadsheet") {
|
||||
const info = args.args[0][0];
|
||||
return [{ ...info, rate: 0.9 }];
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
loader.getDashboard(dashboardId);
|
||||
await animationFrame();
|
||||
const { model } = loader.getDashboard(dashboardId);
|
||||
await waitForDataLoaded(model);
|
||||
expect(await getCellValue(model, "A1")).toBe(0.9);
|
||||
});
|
||||
|
||||
test("Model is in dashboard mode", async () => {
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
loader.getDashboard(3);
|
||||
await animationFrame();
|
||||
const { model } = loader.getDashboard(3);
|
||||
expect(model.config.mode).toBe("dashboard");
|
||||
});
|
||||
|
||||
test("Model is in dashboard mode [2]", async () => {
|
||||
patchWithCleanup(DashboardLoader.prototype, {
|
||||
_activateFirstSheet: () => {
|
||||
expect.step("activate sheet");
|
||||
},
|
||||
});
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
loader.getDashboard(3);
|
||||
await animationFrame();
|
||||
expect.verifySteps(["activate sheet"]);
|
||||
});
|
||||
|
||||
test("default currency format", async () => {
|
||||
onRpc("/spreadsheet/dashboard/data/*", () => ({
|
||||
data: {},
|
||||
revisions: [],
|
||||
default_currency: {
|
||||
code: "Odoo",
|
||||
symbol: "θ",
|
||||
position: "after",
|
||||
decimalPlaces: 2,
|
||||
},
|
||||
}));
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
const result = loader.getDashboard(3);
|
||||
expect(result.status).toBe(Status.Loading);
|
||||
await animationFrame();
|
||||
const { model } = loader.getDashboard(3);
|
||||
expect(model.getters.getCompanyCurrencyFormat()).toBe("#,##0.00[$θ]");
|
||||
});
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
/** @odoo-module */
|
||||
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
import {
|
||||
DashboardLoader,
|
||||
Status,
|
||||
} from "@spreadsheet_dashboard/bundle/dashboard_action/dashboard_loader";
|
||||
import { nextTick, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { getDashboardServerData } from "../utils/data";
|
||||
|
||||
import { waitForDataSourcesLoaded } from "@spreadsheet/../tests/utils/model";
|
||||
import { getCellValue } from "@spreadsheet/../tests/utils/getters";
|
||||
import { RPCError } from "@web/core/network/rpc_service";
|
||||
|
||||
/**
|
||||
* @param {object} [params]
|
||||
* @param {object} [params.serverData]
|
||||
* @param {function} [params.mockRPC]
|
||||
* @returns {Promise<DashboardLoader>}
|
||||
*/
|
||||
async function createDashboardLoader(params = {}) {
|
||||
registry.category("services").add("orm", ormService);
|
||||
const env = await makeTestEnv({
|
||||
serverData: params.serverData || getDashboardServerData(),
|
||||
mockRPC: params.mockRPC,
|
||||
});
|
||||
return new DashboardLoader(env, env.services.orm, async (dashboardId) => {
|
||||
const [record] = await env.services.orm.read(
|
||||
"spreadsheet.dashboard",
|
||||
[dashboardId],
|
||||
["raw"]
|
||||
);
|
||||
return { data: record.raw, revisions: [] };
|
||||
});
|
||||
}
|
||||
|
||||
QUnit.module("spreadsheet_dashboard > Dashboard loader");
|
||||
|
||||
QUnit.test("load all dashboards of all containers", async (assert) => {
|
||||
const loader = await createDashboardLoader();
|
||||
loader.load();
|
||||
assert.deepEqual(loader.getDashboardGroups(), []);
|
||||
await nextTick();
|
||||
assert.deepEqual(loader.getDashboardGroups(), [
|
||||
{
|
||||
id: 1,
|
||||
name: "Container 1",
|
||||
dashboards: [
|
||||
{
|
||||
id: 1,
|
||||
displayName: "Dashboard CRM 1",
|
||||
status: Status.NotLoaded,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
displayName: "Dashboard CRM 2",
|
||||
status: Status.NotLoaded,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Container 2",
|
||||
dashboards: [
|
||||
{
|
||||
id: 3,
|
||||
displayName: "Dashboard Accounting 1",
|
||||
status: Status.NotLoaded,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("load twice does not duplicate spreadsheets", async (assert) => {
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
assert.deepEqual(loader.getDashboardGroups()[1].dashboards, [
|
||||
{ id: 3, displayName: "Dashboard Accounting 1", status: Status.NotLoaded },
|
||||
]);
|
||||
await loader.load();
|
||||
assert.deepEqual(loader.getDashboardGroups()[1].dashboards, [
|
||||
{ id: 3, displayName: "Dashboard Accounting 1", status: Status.NotLoaded },
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("load spreadsheet data", async (assert) => {
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
const result = loader.getDashboard(3);
|
||||
assert.strictEqual(result.status, Status.Loading);
|
||||
await nextTick();
|
||||
assert.strictEqual(result.status, Status.Loaded);
|
||||
assert.ok(result.model);
|
||||
});
|
||||
|
||||
QUnit.test("load spreadsheet data only once", async (assert) => {
|
||||
const loader = await createDashboardLoader({
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "read") {
|
||||
assert.step(`spreadsheet ${args.args[0]} loaded`);
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
let result = loader.getDashboard(3);
|
||||
await nextTick();
|
||||
assert.strictEqual(result.status, Status.Loaded);
|
||||
assert.verifySteps(["spreadsheet 1,2,3 loaded", "spreadsheet 3 loaded"]);
|
||||
result = loader.getDashboard(3);
|
||||
await nextTick();
|
||||
assert.strictEqual(result.status, Status.Loaded);
|
||||
assert.verifySteps([]);
|
||||
});
|
||||
|
||||
QUnit.test("don't return empty dashboard group", async (assert) => {
|
||||
const loader = await createDashboardLoader({
|
||||
mockRPC: async function (route, args) {
|
||||
if (args.method === "search_read" && args.model === "spreadsheet.dashboard.group") {
|
||||
return [
|
||||
{
|
||||
id: 45,
|
||||
name: "Group A",
|
||||
dashboard_ids: [1],
|
||||
},
|
||||
{
|
||||
id: 46,
|
||||
name: "Group B",
|
||||
dashboard_ids: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
assert.deepEqual(loader.getDashboardGroups(), [
|
||||
{
|
||||
id: 45,
|
||||
name: "Group A",
|
||||
dashboards: [
|
||||
{
|
||||
id: 1,
|
||||
displayName: "Dashboard CRM 1",
|
||||
status: Status.NotLoaded,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("load multiple spreadsheets", async (assert) => {
|
||||
const loader = await createDashboardLoader({
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "read") {
|
||||
assert.step(`spreadsheet ${args.args[0]} loaded`);
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
assert.verifySteps(["spreadsheet 1,2,3 loaded"]);
|
||||
loader.getDashboard(1);
|
||||
await nextTick();
|
||||
assert.verifySteps(["spreadsheet 1 loaded"]);
|
||||
loader.getDashboard(2);
|
||||
await nextTick();
|
||||
assert.verifySteps(["spreadsheet 2 loaded"]);
|
||||
loader.getDashboard(1);
|
||||
await nextTick();
|
||||
assert.verifySteps([]);
|
||||
});
|
||||
|
||||
QUnit.test("load spreadsheet data with error", async (assert) => {
|
||||
const loader = await createDashboardLoader({
|
||||
mockRPC: function (route, args) {
|
||||
if (
|
||||
args.method === "read" &&
|
||||
args.model === "spreadsheet.dashboard" &&
|
||||
args.args[1][0] === "raw"
|
||||
) {
|
||||
const error = new RPCError();
|
||||
error.data = { message: "Bip" };
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
const result = loader.getDashboard(3);
|
||||
assert.strictEqual(result.status, Status.Loading);
|
||||
await result.promise.catch(() => assert.step("error"));
|
||||
assert.strictEqual(result.status, Status.Error);
|
||||
assert.strictEqual(result.error.data.message, "Bip");
|
||||
assert.verifySteps(["error"], "error is thrown");
|
||||
});
|
||||
|
||||
QUnit.test("async formulas are correctly evaluated", async (assert) => {
|
||||
const spreadsheetData = {
|
||||
sheets: [
|
||||
{
|
||||
id: "sheet1",
|
||||
cells: {
|
||||
A1: { content: `=ODOO.CURRENCY.RATE("EUR","USD")` }, // an async formula
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const serverData = getDashboardServerData();
|
||||
const dashboardId = 15;
|
||||
serverData.models["spreadsheet.dashboard"].records = [
|
||||
{
|
||||
id: dashboardId,
|
||||
raw: JSON.stringify(spreadsheetData),
|
||||
json_data: JSON.stringify(spreadsheetData),
|
||||
name: "Dashboard Accounting 1",
|
||||
dashboard_group_id: 2,
|
||||
},
|
||||
];
|
||||
serverData.models["spreadsheet.dashboard.group"].records = [
|
||||
{ id: 1, name: "Container 1", dashboard_ids: [dashboardId] },
|
||||
];
|
||||
const loader = await createDashboardLoader({
|
||||
serverData,
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "get_rates_for_spreadsheet") {
|
||||
const info = args.args[0][0];
|
||||
return [{ ...info, rate: 0.9 }];
|
||||
}
|
||||
},
|
||||
});
|
||||
await loader.load();
|
||||
loader.getDashboard(dashboardId);
|
||||
await nextTick();
|
||||
const { model } = loader.getDashboard(dashboardId);
|
||||
await waitForDataSourcesLoaded(model);
|
||||
assert.strictEqual(await getCellValue(model, "A1"), 0.9);
|
||||
});
|
||||
|
||||
QUnit.test("Model is in dashboard mode", async (assert) => {
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
loader.getDashboard(3);
|
||||
await nextTick();
|
||||
const { model } = loader.getDashboard(3);
|
||||
assert.strictEqual(model.config.mode, "dashboard");
|
||||
});
|
||||
|
||||
QUnit.test("Model is in dashboard mode", async (assert) => {
|
||||
patchWithCleanup(DashboardLoader.prototype, {
|
||||
_activateFirstSheet: () => {
|
||||
assert.step("activate sheet");
|
||||
},
|
||||
});
|
||||
const loader = await createDashboardLoader();
|
||||
await loader.load();
|
||||
loader.getDashboard(3);
|
||||
await nextTick();
|
||||
assert.verifySteps(["activate sheet"]);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue