2460 lines
87 KiB
JavaScript
2460 lines
87 KiB
JavaScript
import { beforeEach, describe, destroy, expect, test } from "@odoo/hoot";
|
|
import { queryAllTexts, queryOne } from "@odoo/hoot-dom";
|
|
import { animationFrame, Deferred, mockTimeZone } from "@odoo/hoot-mock";
|
|
import {
|
|
contains,
|
|
defineModels,
|
|
fields,
|
|
findComponent,
|
|
mockService,
|
|
models,
|
|
mountView,
|
|
onRpc,
|
|
patchWithCleanup,
|
|
toggleMenuItem,
|
|
toggleMenuItemOption,
|
|
toggleSearchBarMenu,
|
|
} from "@web/../tests/web_test_helpers";
|
|
import { session } from "@web/session";
|
|
import { MapController } from "@web_map/map_view/map_controller";
|
|
import { MapModel } from "@web_map/map_view/map_model";
|
|
import { MapRenderer } from "@web_map/map_view/map_renderer";
|
|
|
|
const MAP_BOX_TOKEN = "token";
|
|
|
|
function getMapController(view) {
|
|
return findComponent(view, (c) => c instanceof MapController);
|
|
}
|
|
|
|
function getMapRenderer(view) {
|
|
return findComponent(view, (c) => c instanceof MapRenderer);
|
|
}
|
|
|
|
const TEST_RECORDS = {
|
|
task: {
|
|
oneRecord: [{ id: 1, name: "Foo", partner_id: 1 }],
|
|
twoRecordsFieldDateTime: [
|
|
{ id: 1, name: "Foo", scheduled_date: false, partner_id: 1 },
|
|
{
|
|
id: 2,
|
|
name: "Bar",
|
|
scheduled_date: "2022-02-07 21:09:31",
|
|
partner_id: 2,
|
|
},
|
|
],
|
|
twoRecords: [
|
|
{ id: 1, name: "FooProject", sequence: 1, partner_id: 1 },
|
|
{ id: 2, name: "BarProject", sequence: 2, partner_id: 2 },
|
|
],
|
|
threeRecords: [
|
|
{
|
|
id: 1,
|
|
name: "FooProject",
|
|
sequence: 1,
|
|
partner_id: 1,
|
|
partner_ids: [1, 2],
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "BarProject",
|
|
sequence: 2,
|
|
partner_id: 2,
|
|
partner_ids: [1],
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "FooBarProject",
|
|
sequence: 3,
|
|
partner_id: 1,
|
|
partner_ids: [1],
|
|
},
|
|
],
|
|
twoRecordOnePartner: [
|
|
{ id: 1, name: "FooProject", partner_id: 1 },
|
|
{ id: 2, name: "BarProject", partner_id: 1 },
|
|
],
|
|
recordWithouthPartner: [{ id: 1, name: "Foo", partner_id: false }],
|
|
anotherPartnerId: [{ id: 1, name: "FooProject", another_partner_id: 1 }],
|
|
},
|
|
partner: {
|
|
coordinatesNoAddress: [
|
|
{
|
|
id: 1,
|
|
name: "Foo",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
},
|
|
],
|
|
oneLocatedRecord: [
|
|
{
|
|
id: 1,
|
|
name: "Foo",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
sequence: 1,
|
|
},
|
|
],
|
|
wrongCoordinatesNoAddress: [
|
|
{
|
|
id: 1,
|
|
name: "Foo",
|
|
partner_latitude: 10000.0,
|
|
partner_longitude: 100000.5,
|
|
},
|
|
],
|
|
noCoordinatesGoodAddress: [
|
|
{
|
|
id: 1,
|
|
name: "Foo",
|
|
partner_latitude: 0,
|
|
partner_longitude: 0,
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
},
|
|
],
|
|
twoRecordsAddressNoCoordinates: [
|
|
{
|
|
id: 2,
|
|
name: "Foo",
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
sequence: 3,
|
|
},
|
|
{
|
|
id: 1,
|
|
name: "Bar",
|
|
contact_address_complete: "Chaussée de Louvain 94, 5310 Éghezée",
|
|
sequence: 1,
|
|
},
|
|
],
|
|
twoRecordsAddressCoordinates: [
|
|
{
|
|
id: 2,
|
|
name: "Foo",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
sequence: 3,
|
|
},
|
|
{
|
|
id: 1,
|
|
name: "Bar",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
contact_address_complete: "Chaussée de Louvain 94, 5310 Éghezée",
|
|
sequence: 1,
|
|
},
|
|
],
|
|
twoRecordsOneUnlocated: [
|
|
{
|
|
id: 1,
|
|
name: "Foo",
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
sequence: 3,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Bar",
|
|
},
|
|
],
|
|
threeRecords: [
|
|
{
|
|
id: 1,
|
|
name: "Foo",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
sequence: 1,
|
|
user_id: 1,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Bar",
|
|
partner_latitude: 11.0,
|
|
partner_longitude: 11.5,
|
|
contact_address_complete: "Chaussée de Wavre 50, 1367, Ramillies",
|
|
sequence: 2,
|
|
user_id: 2,
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "Baz",
|
|
partner_latitude: 12.0,
|
|
partner_longitude: 12.5,
|
|
contact_address_complete: "Chaussée de Louvain 94, 5310 Éghezée",
|
|
sequence: 3,
|
|
user_id: false,
|
|
},
|
|
],
|
|
unlocatedRecords: [{ id: 1, name: "Foo" }],
|
|
noCoordinatesWrongAddress: [
|
|
{
|
|
id: 1,
|
|
name: "Foo",
|
|
contact_address_complete: "Cfezfezfefes",
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
class Task extends models.Model {
|
|
_name = "project.task";
|
|
|
|
name = fields.Char();
|
|
scheduled_date = fields.Datetime({ string: "Schedule date" });
|
|
task_status = fields.Selection({
|
|
string: "Status",
|
|
selection: [
|
|
["abc", "ABC"],
|
|
["def", "DEF"],
|
|
["ghi", "GHI"],
|
|
],
|
|
});
|
|
sequence = fields.Integer();
|
|
partner_id = fields.Many2one({
|
|
string: "partner",
|
|
relation: "res.partner",
|
|
});
|
|
another_partner_id = fields.Many2one({
|
|
string: "another relation",
|
|
relation: "res.partner",
|
|
});
|
|
partner_ids = fields.One2many({
|
|
string: "Partners",
|
|
comodel_name: "res.partner",
|
|
relation: "res.partner",
|
|
relation_field: "task_id",
|
|
});
|
|
|
|
_records = [{ id: 1, name: "project", partner_id: 1 }];
|
|
}
|
|
|
|
class Users extends models.Model {
|
|
_name = "res.users";
|
|
|
|
name = fields.Char();
|
|
_records = [
|
|
{ id: 1, name: "Mitchell Admin" },
|
|
{ id: 2, name: "Marc Demo" },
|
|
];
|
|
}
|
|
|
|
class Partner extends models.Model {
|
|
_name = "res.partner";
|
|
|
|
name = fields.Char({ string: "Customer" });
|
|
partner_latitude = fields.Float({ string: "Latitude" });
|
|
partner_longitude = fields.Float({ string: "Longitude" });
|
|
contact_address_complete = fields.Char({ string: "Address" });
|
|
task_ids = fields.One2many({
|
|
string: "Task",
|
|
relation: "project.task",
|
|
relation_field: "partner_id",
|
|
});
|
|
sequence = fields.Integer();
|
|
user_id = fields.Many2one({
|
|
string: "Salesperson",
|
|
relation: "res.users",
|
|
});
|
|
|
|
_records = [
|
|
{
|
|
id: 1,
|
|
name: "Foo",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
sequence: 1,
|
|
user_id: 1,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Foo",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
sequence: 3,
|
|
user_id: 2,
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "Bar",
|
|
partner_latitude: 11.0,
|
|
partner_longitude: 11.5,
|
|
contact_address_complete: "Chaussée de Wavre 50, 1367, Ramillies",
|
|
sequence: 4,
|
|
user_id: false,
|
|
},
|
|
];
|
|
|
|
update_latitude_longitude() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
defineModels([Task, Users, Partner]);
|
|
|
|
beforeEach(() => {
|
|
patchWithCleanup(MapModel, {
|
|
// set delay to 0 as _fetchCoordinatesFromAddressOSM is mocked
|
|
COORDINATE_FETCH_DELAY: 0,
|
|
});
|
|
patchWithCleanup(MapModel.prototype, {
|
|
async _fetchCoordinatesFromAddressMB(metaData, data, record) {
|
|
if (metaData.mapBoxToken !== MAP_BOX_TOKEN) {
|
|
return Promise.reject({ status: 401 });
|
|
}
|
|
const coordinates = [];
|
|
coordinates[0] = "10.0";
|
|
coordinates[1] = "10.5";
|
|
const geometry = { coordinates };
|
|
const features = [];
|
|
features[0] = { geometry };
|
|
const successResponse = { features };
|
|
const failResponse = { features: [] };
|
|
switch (record.contact_address_complete) {
|
|
case "Cfezfezfefes":
|
|
return failResponse;
|
|
case "":
|
|
return failResponse;
|
|
}
|
|
return successResponse;
|
|
},
|
|
async _fetchCoordinatesFromAddressOSM(metaData, data, record) {
|
|
const coordinates = [];
|
|
coordinates[0] = { lat: "10.0", lon: "10.5" };
|
|
switch (record.contact_address_complete) {
|
|
case "Cfezfezfefes":
|
|
return [];
|
|
case "":
|
|
return [];
|
|
}
|
|
return coordinates;
|
|
},
|
|
async _fetchRoute(metaData, data) {
|
|
if (metaData.mapBoxToken !== MAP_BOX_TOKEN) {
|
|
return Promise.reject({ status: 401 });
|
|
}
|
|
const legs = [];
|
|
for (let i = 1; i < data.records.length; i++) {
|
|
const coordinates = [];
|
|
coordinates[0] = [10, 10.5];
|
|
coordinates[1] = [10, 10.6];
|
|
const geometry = { coordinates };
|
|
const steps = [];
|
|
steps[0] = { geometry };
|
|
legs.push({ steps: steps });
|
|
}
|
|
const routes = [];
|
|
routes[0] = { legs };
|
|
return { routes };
|
|
},
|
|
_notifyFetchedCoordinate(metaData, data) {
|
|
// do not notify in tests as coords fetching is " synchronous "
|
|
},
|
|
_openStreetMapAPI(metaData, data) {
|
|
// return promise to wait for it
|
|
return this._openStreetMapAPIAsync(metaData, data);
|
|
},
|
|
});
|
|
});
|
|
|
|
describe.tags("desktop");
|
|
describe("map_view_desktop", () => {
|
|
//--------------------------------------------------------------------------
|
|
// Testing data fetching
|
|
//--------------------------------------------------------------------------
|
|
|
|
/**
|
|
* data: no record
|
|
* Should have no record
|
|
* Should have no marker
|
|
* Should have no route
|
|
*/
|
|
test("Create a view with no record", async () => {
|
|
expect.assertions(6);
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
Task._records = [];
|
|
onRpc("project.task", "web_search_read", ({ kwargs }) => {
|
|
const specification = kwargs.specification;
|
|
expect(specification.partner_id).toEqual({
|
|
fields: {
|
|
contact_address_complete: {},
|
|
display_name: {},
|
|
partner_latitude: {},
|
|
partner_longitude: {},
|
|
},
|
|
});
|
|
expect(specification.name).toEqual({});
|
|
});
|
|
onRpc("res.partner", "search_read", () => {
|
|
throw new Error("Should not search_read the partners if there are no partner");
|
|
});
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `
|
|
<map res_partner="partner_id" routing="1">
|
|
<field name="name" string="Project"/>
|
|
<field name="partner_ids" string="Project"/>
|
|
</map>
|
|
`,
|
|
});
|
|
expect("a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1",
|
|
{
|
|
message: "The link's URL should not contain any coordinates",
|
|
}
|
|
);
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "No marker should be on a the map.",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: one record that has no partner linked to it
|
|
* The record shouldn't be kept and displayed in the list of records
|
|
* should have no marker
|
|
* Should have no route
|
|
*/
|
|
test("Create a view with one record that has no partner", async () => {
|
|
Task._records = TEST_RECORDS.task.recordWithouthPartner;
|
|
Partner._records = [];
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "No marker should be on a the map.",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: one record that has a partner which has coordinates but no address
|
|
* One record
|
|
* One marker
|
|
* no route
|
|
*/
|
|
test("Create a view with one record and a partner located by coordinates", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.coordinatesNoAddress;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect("div.leaflet-marker-icon").toHaveCount(1, {
|
|
message: "There should be one marker on the map",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(1);
|
|
});
|
|
|
|
/**
|
|
* data: one record linked to one partner with no address and wrong coordinates
|
|
* api: MapBox
|
|
* record shouldn't be kept and displayed in the list
|
|
* no route
|
|
* no marker
|
|
*/
|
|
test("Create view with one record linked to a partner with wrong coordinates with MB", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.wrongCoordinatesNoAddress;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "There should be np marker on the map",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: one record linked to one partner with no address and wrong coordinates
|
|
* api: OpenStreet Map
|
|
* record should be kept
|
|
* no route
|
|
* no marker
|
|
*/
|
|
test("Create view with one record linked to a partner with wrong coordinates with OSM", async () => {
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.wrongCoordinatesNoAddress;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "There should be no marker on the map",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(0);
|
|
});
|
|
/**
|
|
* data: one record linked to one partner with no coordinates and good address
|
|
* api: OpenStreet Map
|
|
* caching RPC called, assert good args
|
|
* one record
|
|
* no route
|
|
*/
|
|
test("Create View with one record linked to a partner with no coordinates and right address OSM", async () => {
|
|
expect.assertions(5);
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.noCoordinatesGoodAddress;
|
|
|
|
onRpc("res.partner", "update_latitude_longitude", ({ args }) => {
|
|
expect(args[0]).toHaveLength(1, {
|
|
message: "There should be one record needing caching",
|
|
});
|
|
expect(args[0][0].id).toBe(1, { message: "The records's id should be 1" });
|
|
return {};
|
|
});
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(1);
|
|
expect("div.leaflet-marker-icon").toHaveCount(1, {
|
|
message: "There should be one marker on the map",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: one record linked to one partner with no coordinates and good address
|
|
* api: MapBox
|
|
* caching RPC called, assert good args
|
|
* one record
|
|
* no route
|
|
*/
|
|
test("Create View with one record linked to a partner with no coordinates and right address MB", async () => {
|
|
expect.assertions(5);
|
|
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.noCoordinatesGoodAddress;
|
|
onRpc("res.partner", "update_latitude_longitude", ({ args }) => {
|
|
expect(args[0]).toHaveLength(1, {
|
|
message: "There should be one record needing caching",
|
|
});
|
|
expect(args[0][0].id).toBe(1, { message: "The records's id should be 1" });
|
|
return {};
|
|
});
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(1);
|
|
expect("div.leaflet-marker-icon").toHaveCount(1, {
|
|
message: "There should be one marker on the map",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: one record linked to a partner with no coordinates and no address
|
|
* api: MapBox
|
|
* 1 record
|
|
* no route
|
|
* no marker
|
|
*/
|
|
test("Create view with no located record", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.unlocatedRecords;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(0);
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "No marker should be on a the map.",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: one record linked to a partner with no coordinates and no address
|
|
* api: OSM
|
|
* one record
|
|
* no route
|
|
* no marker
|
|
*/
|
|
test("Create view with no located record OSM", async () => {
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.unlocatedRecords;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(0);
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "No marker should be on a the map.",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: one record linked to a partner with no coordinates and wrong address
|
|
* api: OSM
|
|
* one record
|
|
* no route
|
|
* no marker
|
|
*/
|
|
test("Create view with no badly located record OSM", async () => {
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.noCoordinatesWrongAddress;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(0);
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "No marker should be on a the map.",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: one record linked to a partner with no coordinates and wrong address
|
|
* api: mapbox
|
|
* one record
|
|
* no route
|
|
* no marker
|
|
*/
|
|
|
|
test("Create view with no badly located record MB", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.noCoordinatesWrongAddress;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(0);
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "No marker should be on a the map.",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* data: 2 records linked to the same partner
|
|
* 2 records
|
|
* 2 markers
|
|
* no route
|
|
* same partner object
|
|
* 1 caching request
|
|
*/
|
|
test("Create a view with two located records same partner", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecordOnePartner;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(2);
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(1);
|
|
});
|
|
|
|
/**
|
|
* data: 2 records linked to differnet partners
|
|
* 2 records
|
|
* 1 route
|
|
* different partner object.
|
|
* 2 caching
|
|
*/
|
|
test("Create a view with two located records different partner", async () => {
|
|
expect.assertions(5);
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
onRpc("res.partner", "update_latitude_longitude", ({ args }) => {
|
|
expect(args[0]).toHaveLength(2, {
|
|
message: "Should have 2 record needing caching",
|
|
});
|
|
return {};
|
|
});
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(2);
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(1);
|
|
expect(controller.model.data.records[0].partner).not.toBe(
|
|
controller.model.data.records[1].partner,
|
|
{ message: "The records should have the same partner object as a property" }
|
|
);
|
|
});
|
|
|
|
/**
|
|
* data: 2 valid res.partner records
|
|
* test the case where the model is res.partner and the "res.partner" field is the id
|
|
* should have 2 records,
|
|
* 2 markers
|
|
* no route
|
|
*/
|
|
test("Create a view with res.partner", async () => {
|
|
expect.assertions(4);
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Partner._records = [
|
|
{
|
|
id: 2,
|
|
name: "Foo",
|
|
contact_address_complete: "Chaussée de Namur 40, 1367, Ramillies",
|
|
sequence: 3,
|
|
},
|
|
{
|
|
id: 1,
|
|
name: "FooBar",
|
|
contact_address_complete: "Chaussée de Louvain 94, 5310 Éghezée",
|
|
sequence: 1,
|
|
},
|
|
];
|
|
onRpc("res.partner", "web_search_read", ({ kwargs }) => {
|
|
expect(kwargs.specification).toEqual({
|
|
contact_address_complete: {},
|
|
display_name: {},
|
|
id: {},
|
|
partner_latitude: {},
|
|
partner_longitude: {},
|
|
});
|
|
});
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "res.partner",
|
|
arch: `<map res_partner="id" />`,
|
|
});
|
|
expect(
|
|
".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
|
|
).toHaveCount(2);
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
});
|
|
|
|
/**
|
|
* Data: 3 partner records with user_id used as groupBy
|
|
* Test if the map view displays the many2one field's name as the group name
|
|
*/
|
|
test("Create a view with many2one groupBy and res.partner model", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Partner._records = TEST_RECORDS.partner.threeRecords;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "res.partner",
|
|
arch: `<map res_partner="id" />`,
|
|
groupBy: ["user_id"],
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(3, {
|
|
message: "Should have 3 groups",
|
|
});
|
|
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-group-header")).toEqual(
|
|
["Mitchell Admin", "Marc Demo", "None"],
|
|
{
|
|
message: "Should have correct group headers",
|
|
}
|
|
);
|
|
|
|
expect(".o-map-renderer--pin-list-details").toHaveCount(3, {
|
|
message: "Should have 3 group detail sections",
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-details li").toHaveCount(3, {
|
|
message: "Should have 3 total records across all groups",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* data: 3 records linked to one located partner and one unlocated
|
|
* test if only the 2 located records are displayed
|
|
*/
|
|
test("Create a view with 2 located records and 1 unlocated", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.threeRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsOneUnlocated;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.data.records.length).toBe(3);
|
|
expect(controller.model.data.records[0].partner.id).toBe(1, {
|
|
message: "The partner's id should be 1",
|
|
});
|
|
expect(controller.model.data.records[1].partner.id).toBe(2, {
|
|
message: "The partner's id should be 2",
|
|
});
|
|
expect(controller.model.data.records[2].partner.id).toBe(1, {
|
|
message: "The partner's id should be 1",
|
|
});
|
|
});
|
|
|
|
test("Change load limit", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.threeRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" limit="2" />`,
|
|
});
|
|
expect(`.o_pager_counter .o_pager_value`).toHaveText("1-2");
|
|
expect(`.o_pager_counter span.o_pager_limit`).toHaveText("3");
|
|
});
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Renderer testing
|
|
//--------------------------------------------------------------------------
|
|
|
|
test("Google Maps redirection", async () => {
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id"></map>`,
|
|
});
|
|
|
|
expect("a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&waypoints=Chauss%C3%A9e%20de%20Louvain%2094%2C%205310%20%C3%89ghez%C3%A9e|Chauss%C3%A9e%20de%20Namur%2040%2C%201367%2C%20Ramillies",
|
|
{ message: "The link's URL should contain the address" }
|
|
);
|
|
|
|
await contains(".leaflet-marker-icon").click();
|
|
expect("div.leaflet-popup a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&destination=Chauss%C3%A9e%20de%20Namur%2040%2C%201367%2C%20Ramillies",
|
|
{ message: "The URL of the link should contain the address" }
|
|
);
|
|
});
|
|
|
|
test("Google Maps redirection (with routing = true)", async () => {
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1"></map>`,
|
|
});
|
|
|
|
expect("a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&destination=Chauss%C3%A9e%20de%20Namur%2040%2C%201367%2C%20Ramillies&waypoints=Chauss%C3%A9e%20de%20Louvain%2094%2C%205310%20%C3%89ghez%C3%A9e",
|
|
{ message: "The link's URL should contain the address" }
|
|
);
|
|
|
|
await contains(".leaflet-marker-icon").click();
|
|
expect("div.leaflet-popup a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&destination=Chauss%C3%A9e%20de%20Namur%2040%2C%201367%2C%20Ramillies",
|
|
{ message: "The URL of the link should contain the address" }
|
|
);
|
|
});
|
|
|
|
test("Unicity of coordinates in Google Maps url", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecordOnePartner;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
});
|
|
expect("a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&waypoints=Chauss%C3%A9e%20de%20Louvain%2094%2C%205310%20%C3%89ghez%C3%A9e",
|
|
{ message: "The link's URL should contain the address" }
|
|
);
|
|
await contains(".leaflet-marker-icon").click();
|
|
expect("div.leaflet-popup a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&destination=Chauss%C3%A9e%20de%20Louvain%2094%2C%205310%20%C3%89ghez%C3%A9e",
|
|
{ message: "The URL of the link should contain the address" }
|
|
);
|
|
});
|
|
|
|
test("test the position of pin", async () => {
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
});
|
|
|
|
expect(".o-map-renderer--marker").toHaveCount(1, {
|
|
message: "Should have one marker created",
|
|
});
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
const renderer = getMapRenderer(view);
|
|
expect(renderer.markers[0].getLatLng().lat).toBe(10, {
|
|
message: "The latitude should be the same as the record",
|
|
});
|
|
expect(renderer.markers[0].getLatLng().lng).toBe(10.5, {
|
|
message: "The longitude should be the same as the record",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* data: two located records
|
|
* Create an empty map
|
|
*/
|
|
test("Create of a empty map", async () => {
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "res.partner",
|
|
arch: `<map />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.resPartnerField).toBe(null, {
|
|
message: "the resPartnerField should not be set",
|
|
});
|
|
|
|
expect(".o_map_view").toHaveClass("o_view_controller");
|
|
expect(".leaflet-map-pane").toHaveCount(1, {
|
|
message: "If the map exists this div should exist",
|
|
});
|
|
expect(".leaflet-pane .leaflet-tile-pane > *").toHaveCount(1, {
|
|
message: "The map tiles should have been happened to the DOM",
|
|
});
|
|
// if element o-map-renderer--container has class leaflet-container then
|
|
// the map is mounted
|
|
expect(".o-map-renderer--container").toHaveClass("leaflet-container", {
|
|
message: "the map should be in the DOM",
|
|
});
|
|
|
|
expect(".leaflet-overlay-pane > *").toHaveCount(0, {
|
|
message: "Should have no showing route",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* two located records
|
|
* without routing or default_order
|
|
* normal marker icon
|
|
* test the click on them
|
|
*/
|
|
|
|
test("Create view with normal marker icons", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.numbering).toBe(false, {
|
|
message: "the numbering option should not be enabled",
|
|
});
|
|
expect(controller.model.metaData.routing).toBe(false, {
|
|
message: "The routing option should not be enabled",
|
|
});
|
|
|
|
expect(".leaflet-marker-icon").toHaveCount(1, { message: "There should be 1 marker" });
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
|
|
await contains(".leaflet-marker-icon").click();
|
|
|
|
expect(".leaflet-popup-pane > *").toHaveCount(1, {
|
|
message: "Should have one showing popup",
|
|
});
|
|
|
|
await contains("div.leaflet-container").click();
|
|
// wait for the popup's destruction which takes a certain time...
|
|
for (let i = 0; i < 15; i++) {
|
|
await animationFrame();
|
|
}
|
|
|
|
expect(".leaflet-popup-pane > *").toHaveCount(0, {
|
|
message: "Should not have any showing popup",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* two located records
|
|
* with default_order
|
|
* no numbered icon
|
|
* test click on them
|
|
* asserts that the rpc receive the right parameters
|
|
*/
|
|
|
|
test("Create a view with default_order", async () => {
|
|
expect.assertions(7);
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
onRpc("project.task", "web_search_read", ({ kwargs }) => {
|
|
expect(kwargs.order).toBe("name ASC", {
|
|
message: "The sorting order should be on the field name in a ascendant way",
|
|
});
|
|
});
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" default_order="name" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.numbering).toBe(false, {
|
|
message: "the numbering option should not be enabled",
|
|
});
|
|
expect(controller.model.metaData.routing).toBe(false, {
|
|
message: "The routing option should not be enabled",
|
|
});
|
|
expect("div.leaflet-marker-icon").toHaveCount(1, { message: "There should be 1 marker" });
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
expect(".leaflet-popup-pane > *").toHaveCount(0, {
|
|
message: "Should have no showing popup",
|
|
});
|
|
await contains("div.leaflet-marker-icon").click();
|
|
expect(".leaflet-popup-pane > *").toHaveCount(1, {
|
|
message: "Should have one showing popup",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* two locted records
|
|
* with routing enabled
|
|
* numbered icon
|
|
* test click on route
|
|
*/
|
|
|
|
test("Create a view with routing", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.numbering).toBe(true, {
|
|
message: "the numbering option should be enabled",
|
|
});
|
|
expect(controller.model.metaData.routing).toBe(true, {
|
|
message: "The routing option should be enabled",
|
|
});
|
|
|
|
expect(controller.model.data.numberOfLocatedRecords).toBe(2, {
|
|
message: "Should have 2 located Records",
|
|
});
|
|
expect(controller.model.data.routes.length).toBe(1, {
|
|
message: "Should have 1 computed route",
|
|
});
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
expect("path.leaflet-interactive").toHaveAttribute("stroke", "blue", {
|
|
message: "The route should be blue if it has not been clicked",
|
|
});
|
|
expect("path.leaflet-interactive").toHaveAttribute("stroke-opacity", "0.3", {
|
|
message: "The opacity of the polyline should be 0.3",
|
|
});
|
|
// the element isn't visible
|
|
await contains("path.leaflet-interactive", { visible: false }).click();
|
|
await animationFrame();
|
|
expect("path.leaflet-interactive").toHaveAttribute("stroke", "darkblue", {
|
|
message: "The route should be darkblue after being clicked",
|
|
});
|
|
expect("path.leaflet-interactive").toHaveAttribute("stroke-opacity", "1", {
|
|
message: "The opacity of the polyline should be 1",
|
|
});
|
|
});
|
|
|
|
test("Resequence records", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
const taskRecords = [
|
|
{
|
|
id: 1,
|
|
name: "Project 1",
|
|
sequence: 3,
|
|
partner_id: 1,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Project 2",
|
|
sequence: 4,
|
|
partner_id: 1,
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "Project 3",
|
|
sequence: 5,
|
|
partner_id: 1,
|
|
},
|
|
{
|
|
id: 4,
|
|
name: "Project 4",
|
|
sequence: 6,
|
|
partner_id: 1,
|
|
},
|
|
{
|
|
id: 5,
|
|
name: "Project 5",
|
|
sequence: 7,
|
|
partner_id: 1,
|
|
},
|
|
];
|
|
Task._records = taskRecords;
|
|
|
|
let resequenceCalled = false;
|
|
|
|
patchWithCleanup(MapModel.prototype, {
|
|
async _maxBoxAPI(metaData, data) {
|
|
if (resequenceCalled) {
|
|
// Verify that the API is called with the records correctly ordered
|
|
const ids = data.records.map((record) => record.id);
|
|
expect.step(`API called ${ids}`);
|
|
}
|
|
},
|
|
});
|
|
|
|
onRpc("web_resequence", ({ model, args, kwargs }) => {
|
|
const [ids] = args;
|
|
const { field_name: fieldName, offset } = kwargs;
|
|
expect.step(`resequence ${model} ${fieldName} ${offset} ${ids}`);
|
|
resequenceCalled = true;
|
|
});
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `
|
|
<map res_partner="partner_id" routing="1" default_order="sequence" allow_resequence="true">
|
|
<field name="sequence"/>
|
|
</map>
|
|
`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.allowResequence).toBe(true, {
|
|
message: "The resequence option should be activated",
|
|
});
|
|
expect(controller.model.metaData.defaultOrder[0].name).toBe("sequence");
|
|
|
|
expect(".o_row_handle").toHaveCount(5, {
|
|
message: "Should have one row handle per record",
|
|
});
|
|
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details li")).toEqual([
|
|
"1. Project 1",
|
|
"2. Project 2",
|
|
"3. Project 3",
|
|
"4. Project 4",
|
|
"5. Project 5",
|
|
]);
|
|
|
|
await contains(".o-map-renderer--pin-located:nth-child(2) .o_row_handle").dragAndDrop(
|
|
".o-map-renderer--pin-located:nth-child(4) .o_row_handle"
|
|
);
|
|
await animationFrame();
|
|
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details li")).toEqual([
|
|
"1. Project 1",
|
|
"2. Project 3",
|
|
"3. Project 4",
|
|
"4. Project 2",
|
|
"5. Project 5",
|
|
]);
|
|
|
|
expect.verifySteps(["resequence project.task sequence 4 3,4,2", "API called 1,3,4,2,5"]);
|
|
});
|
|
|
|
test("Resequence records, multiple default order fields", async () => {
|
|
const taskRecords = [
|
|
{
|
|
id: 1,
|
|
name: "Project 1",
|
|
sequence: 3,
|
|
partner_id: 1,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Project 2",
|
|
sequence: 3,
|
|
partner_id: 1,
|
|
},
|
|
];
|
|
Task._records = taskRecords;
|
|
const defer = new Deferred();
|
|
onRpc("web_resequence", async ({ model, args, kwargs }) => {
|
|
const [ids] = args;
|
|
const { field_name: fieldName, offset } = kwargs;
|
|
await defer;
|
|
expect.step(`resequence ${model} ${fieldName} ${offset} ${ids}`);
|
|
});
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `
|
|
<map res_partner="partner_id" routing="1" default_order="sequence, id" allow_resequence="true">
|
|
<field name="sequence"/>
|
|
<field name="id"/>
|
|
</map>
|
|
`,
|
|
});
|
|
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details li")).toEqual([
|
|
"1. Project 1",
|
|
"2. Project 2",
|
|
]);
|
|
|
|
await contains(".o-map-renderer--pin-located:nth-child(1) .o_row_handle").dragAndDrop(
|
|
".o-map-renderer--pin-located:nth-child(2) .o_row_handle"
|
|
);
|
|
await animationFrame();
|
|
|
|
// Model got notified before the backend resequence call
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details li")).toEqual([
|
|
"1. Project 2",
|
|
"2. Project 1",
|
|
]);
|
|
|
|
defer.resolve();
|
|
await animationFrame();
|
|
expect.verifySteps(["resequence project.task sequence 3 2,1"]);
|
|
});
|
|
|
|
test("When resequencing, model get notified before the backend call", async () => {
|
|
const taskRecords = [
|
|
{
|
|
id: 1,
|
|
name: "Project 1",
|
|
sequence: 3,
|
|
partner_id: 1,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Project 2",
|
|
sequence: 4,
|
|
partner_id: 1,
|
|
},
|
|
];
|
|
Task._records = taskRecords;
|
|
const defer = new Deferred();
|
|
onRpc("web_resequence", async ({ model, args, kwargs }) => {
|
|
const [ids] = args;
|
|
const { field_name: fieldName, offset } = kwargs;
|
|
await defer;
|
|
expect.step(`resequence ${model} ${fieldName} ${offset} ${ids}`);
|
|
});
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `
|
|
<map res_partner="partner_id" routing="1" default_order="sequence" allow_resequence="true">
|
|
<field name="sequence"/>
|
|
</map>
|
|
`,
|
|
});
|
|
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details li")).toEqual([
|
|
"1. Project 1",
|
|
"2. Project 2",
|
|
]);
|
|
|
|
await contains(".o-map-renderer--pin-located:nth-child(1) .o_row_handle").dragAndDrop(
|
|
".o-map-renderer--pin-located:nth-child(2) .o_row_handle"
|
|
);
|
|
await animationFrame();
|
|
|
|
// Model got notified before the backend resequence call
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details li")).toEqual([
|
|
"1. Project 2",
|
|
"2. Project 1",
|
|
]);
|
|
|
|
defer.resolve();
|
|
await animationFrame();
|
|
expect.verifySteps(["resequence project.task sequence 3 2,1"]);
|
|
});
|
|
|
|
test("Create a view with routingError", async () => {
|
|
expect.assertions(1);
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
patchWithCleanup(MapModel.prototype, {
|
|
async _maxBoxAPI(metaData, data) {
|
|
data.routingError = "this is test warning";
|
|
data.routes = [];
|
|
},
|
|
});
|
|
|
|
Task._records = [
|
|
{ id: 1, name: "FooProject", sequence: 1 },
|
|
{ id: 2, name: "BarProject", sequence: 2 },
|
|
];
|
|
Partner._records = [];
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
|
|
expect(".o-map-renderer .o-map-renderer--alert").toHaveCount(1, {
|
|
message: "should have alert",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* routing with token and one located record
|
|
* No route
|
|
*/
|
|
test("create a view with routing and one located record", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.routing).toBe(true, {
|
|
message: "The routing option should be enabled",
|
|
});
|
|
expect(controller.model.data.routes.length).toBe(0, {
|
|
message: "Should have no computed route",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* no mapbox token
|
|
* assert that the view uses the right api and routes
|
|
*/
|
|
test("CreateView with empty mapbox token setting", async () => {
|
|
Task._records = TEST_RECORDS.task.recordWithouthPartner;
|
|
Partner._records = [];
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.mapBoxToken).toBe("", {
|
|
message: "The token should be an empty string",
|
|
});
|
|
expect(controller.model.data.useMapBoxAPI).toBe(false, {
|
|
message: "model should not use mapbox",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* wrong mapbox token
|
|
* assert that the view uses the openstreetmap api
|
|
*/
|
|
test("Create a view with wrong map box setting", async () => {
|
|
patchWithCleanup(session, { map_box_token: "vrve" });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.mapBoxToken).toBe("vrve", {
|
|
message: "The token should be kept",
|
|
});
|
|
expect(controller.model.data.useMapBoxAPI).toBe(false, {
|
|
message: "model should not use mapbox",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* wrong mapbox token fails at catch at route computing
|
|
*/
|
|
test("create a view with wrong map box setting and located records", async () => {
|
|
patchWithCleanup(session, { map_box_token: "frezfre" });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.mapBoxToken).toBe("frezfre", {
|
|
message: "The token should be kept",
|
|
});
|
|
expect(controller.model.data.useMapBoxAPI).toBe(false, {
|
|
message: "model should not use mapbox",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* create view with right map box token
|
|
* assert that the view uses the map box api
|
|
*/
|
|
test("Create a view with the right map box token", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.recordWithouthPartner;
|
|
Partner._records = [];
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.mapBoxToken).toBe("token", {
|
|
message: "The token should be the right token",
|
|
});
|
|
expect(controller.model.data.useMapBoxAPI).toBe(true, {
|
|
message: "model should not use mapbox",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* data: two located records
|
|
*/
|
|
|
|
test("Click on pin shows popup, click on another shuts the first and open the other", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
expect(".leaflet-pane .leaflet-popup-pane > *").toHaveCount(0, {
|
|
message: "The popup div should be empty",
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
expect(".leaflet-popup-pane > *").toHaveCount(1, {
|
|
message: "The popup div should contain one element",
|
|
});
|
|
|
|
// the element isn't visible
|
|
await contains(".leaflet-map-pane", { visible: false }).click();
|
|
await animationFrame();
|
|
// wait for the popup's destruction which takes a certain time...
|
|
for (let i = 0; i < 15; i++) {
|
|
await animationFrame();
|
|
}
|
|
expect(".leaflet-pane .leaflet-popup-pane > *").toHaveCount(0, {
|
|
message: "The popup div should be empty",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* data: two located records
|
|
* asserts that all the records are shown on the map
|
|
*/
|
|
test("assert that all the records are shown on the map", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
const mapX = queryOne(".leaflet-map-pane")._leaflet_pos.x;
|
|
const mapY = queryOne(".leaflet-map-pane")._leaflet_pos.y;
|
|
expect(mapX - queryOne("div.leaflet-marker-icon")._leaflet_pos.x).toBeLessThan(0, {
|
|
message:
|
|
"If the marker is currently shown on the map, the subtraction of latitude should be under 0",
|
|
});
|
|
expect(mapY - queryOne("div.leaflet-marker-icon")._leaflet_pos.y).toBeLessThan(0);
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* data: two located records
|
|
* asserts that the right fields are shown in the popup
|
|
*/
|
|
|
|
test("Content of the marker popup with one field", async () => {
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
const view = await mountView({
|
|
config: { views: [[false, "form"]] },
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `
|
|
<map res_partner="partner_id" routing="1" hide_name="1" hide_address="1">
|
|
<field name="name" string="Name" />
|
|
</map>
|
|
`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.metaData.fieldNamesMarkerPopup[0].fieldName).toBe("name");
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
|
|
expect(controller.model.metaData.fieldNamesMarkerPopup.length).toBe(1, {
|
|
message: "fieldsMarkerPopup should contain one field",
|
|
});
|
|
expect("tbody tr").toHaveCount(1, { message: "The popup should have one field" });
|
|
expect("tbody tr").toHaveText("Name Foo", {
|
|
message: "Field row's text should be 'Name Foo'",
|
|
});
|
|
expect(".o-map-renderer--popup-buttons > *").toHaveCount(3, {
|
|
message: "The popup should contain 2 buttons and one divider",
|
|
});
|
|
});
|
|
|
|
test("Content of the marker popup with date time", async () => {
|
|
mockTimeZone(+2); // UTC+2
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecordsFieldDateTime;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates.map((r) => ({ ...r }));
|
|
Partner._records[0].partner_latitude = 11.0;
|
|
|
|
await mountView({
|
|
config: { views: [[false, "form"]] },
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="true" hide_name="true" hide_address="true">
|
|
<field name="scheduled_date" string="Date"/>
|
|
</map>`,
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon:first-child").click();
|
|
|
|
expect("tbody tr .o-map-renderer--popup-table-content-value").toHaveCount(0, {
|
|
message: "It should not contains a value node because it's not scheduled",
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon:last-child").click();
|
|
|
|
expect("tbody tr .o-map-renderer--popup-table-content-value").toHaveText(
|
|
"2022-02-07 23:09:31",
|
|
{ message: 'The time "2022-02-07 21:09:31" should be in the local timezone' }
|
|
);
|
|
});
|
|
|
|
/**
|
|
* data: two located records
|
|
* asserts that no field is shown in popup
|
|
*/
|
|
|
|
test("Content of the marker with no field", async () => {
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressNoCoordinates;
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
await mountView({
|
|
config: { views: [[false, "form"]] },
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" hide_name="1" hide_address="1" />`,
|
|
});
|
|
await contains("div.leaflet-marker-icon").click();
|
|
|
|
expect("tbody > *").toHaveCount(0, {
|
|
message: "The popup should have only the button",
|
|
});
|
|
expect(".o-map-renderer--popup-buttons > *").toHaveCount(3, {
|
|
message: "The popup should contain 2 buttons and one divider",
|
|
});
|
|
});
|
|
|
|
test("Attribute: hide_name", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" hide_name="1" />`,
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
|
|
expect("tbody > tr").toHaveCount(1, { message: "The popup should have one field" });
|
|
expect("tbody tr .o-map-renderer--popup-table-content-name").toHaveText("Address", {
|
|
message: "The popup should have address field",
|
|
});
|
|
});
|
|
|
|
test("Render partner address field in popup", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" hide_name="1" />`,
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
|
|
expect("tbody tr").toHaveCount(1, { message: "The popup should have one field" });
|
|
expect("tbody tr .o-map-renderer--popup-table-content-name").toHaveText("Address", {
|
|
message: "The popup should have address field",
|
|
});
|
|
expect("tbody tr .o-map-renderer--popup-table-content-value").toHaveText(
|
|
"Chaussée de Namur 40, 1367, Ramillies",
|
|
{ message: "The popup should have correct address" }
|
|
);
|
|
});
|
|
|
|
test("Hide partner address field in popup", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" hide_address="1" />`,
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
|
|
expect("tbody tr").toHaveCount(1, { message: "The popup should have one field" });
|
|
expect("tbody tr .o-map-renderer--popup-table-content-name").toHaveText("Name", {
|
|
message: "The popup should have name field",
|
|
});
|
|
expect("tbody tr .o-map-renderer--popup-table-content-value").toHaveText("Foo", {
|
|
message: "The popup should have correct address",
|
|
});
|
|
});
|
|
|
|
test("Handle records of same co-ordinates in marker", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.twoRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
});
|
|
|
|
expect("div.leaflet-marker-icon").toHaveCount(1, {
|
|
message: "There should be a one marker",
|
|
});
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
|
|
expect("tbody tr").toHaveCount(1, { message: "The popup should have one field" });
|
|
expect("tbody tr .o-map-renderer--popup-table-content-name").toHaveText("Address", {
|
|
message: "The popup should have address field",
|
|
});
|
|
});
|
|
|
|
test("Pager", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = Array.from({ length: 101 }, (_, index) => ({
|
|
id: index + 1,
|
|
name: "project",
|
|
partner_id: index + 1,
|
|
}));
|
|
Partner._records = Array.from({ length: 101 }, (_, index) => ({
|
|
id: index + 1,
|
|
name: "Foo",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
}));
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
});
|
|
expect(".o_pager").toHaveCount(1);
|
|
expect(`.o_pager_counter .o_pager_value`).toHaveText("1-80", {
|
|
message: "current pager value should be 1-20",
|
|
});
|
|
expect(`.o_pager_counter span.o_pager_limit`).toHaveText("101", {
|
|
message: "current pager limit should be 21",
|
|
});
|
|
|
|
await contains(`.o_pager button.o_pager_next`).click();
|
|
|
|
expect(`.o_pager_counter .o_pager_value`).toHaveText("81-101", {
|
|
message: "pager value should be 21-40",
|
|
});
|
|
});
|
|
|
|
test("New domain", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = [
|
|
{ id: 1, name: "FooProject", sequence: 1, partner_id: 1 },
|
|
{ id: 2, name: "BarProject", sequence: 2, partner_id: 2 },
|
|
];
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
searchViewArch: `
|
|
<search>
|
|
<filter name="f_1" string="Filter 1" domain="[('name', '=', 'FooProject')]"/>
|
|
<filter name="f_2" string="Filter 2" domain="[('name', '=', 'Foofezfezf')]"/>
|
|
<filter name="f_3" string="Filter 3" domain="[('name', 'like', 'Project')]"/>
|
|
</search>
|
|
`,
|
|
});
|
|
const controller = getMapController(view);
|
|
expect(controller.model.data.records).toHaveLength(2, {
|
|
message: "There should be 2 records",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(1);
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
|
|
await toggleSearchBarMenu();
|
|
await toggleMenuItem("Filter 1");
|
|
|
|
expect(controller.model.data.records).toHaveLength(1, {
|
|
message: "There should be 1 record",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
expect("div.leaflet-marker-icon").toHaveCount(1, {
|
|
message: "There should be 1 marker on the map",
|
|
});
|
|
|
|
await toggleMenuItem("Filter 1");
|
|
await toggleMenuItem("Filter 2");
|
|
|
|
expect(controller.model.data.records).toHaveLength(0, {
|
|
message: "There should be no record",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(0);
|
|
expect("div.leaflet-marker-icon").toHaveCount(0, {
|
|
message: "There should be 0 marker on the map",
|
|
});
|
|
|
|
await toggleMenuItem("Filter 2");
|
|
await toggleMenuItem("Filter 3");
|
|
|
|
expect(controller.model.data.records).toHaveLength(2, {
|
|
message: "There should be 2 record",
|
|
});
|
|
expect(".leaflet-overlay-pane path").toHaveCount(1);
|
|
expect("div.leaflet-marker-icon").toHaveCount(1, {
|
|
message: "There should be 1 marker on the map",
|
|
});
|
|
expect("div.leaflet-marker-icon .o-map-renderer--marker-badge").toHaveText("2", {
|
|
message: "There should be a marker for two records",
|
|
});
|
|
});
|
|
|
|
test("Toggle grouped pin lists", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.threeRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
groupBy: ["partner_id"],
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(2, {
|
|
message: "Should have 2 groups",
|
|
});
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-group-header")).toEqual(["Bar", "Foo"]);
|
|
expect(".o-map-renderer--pin-list-details").toHaveCount(2);
|
|
expect(".o-map-renderer--pin-list-details li").toHaveCount(3);
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details")).toEqual([
|
|
"FooProject\nFooBarProject",
|
|
"BarProject",
|
|
]);
|
|
|
|
await contains(".o-map-renderer--pin-list-group-header:eq(1)").click();
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(2, {
|
|
message: "Should still have 2 groups",
|
|
});
|
|
expect(".o-map-renderer--pin-list-details").toHaveCount(1);
|
|
expect(".o-map-renderer--pin-list-details li").toHaveCount(2);
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details")).toEqual([
|
|
"FooProject\nFooBarProject",
|
|
]);
|
|
|
|
await contains(".o-map-renderer--pin-list-group-header:eq(0)").click();
|
|
|
|
expect(".o-map-renderer--pin-list-details").toHaveCount(0);
|
|
|
|
await contains(".o-map-renderer--pin-list-group-header:eq(1)").click();
|
|
|
|
expect(".o-map-renderer--pin-list-details").toHaveCount(1);
|
|
expect(".o-map-renderer--pin-list-details li").toHaveCount(1);
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details")).toEqual(["BarProject"]);
|
|
});
|
|
|
|
test("Toggle grouped one2many pin lists", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.threeRecords.map((r) => ({ ...r }));
|
|
Task._records[1].partner_ids = [1, 3];
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id"/>`,
|
|
groupBy: ["partner_ids"],
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(3, {
|
|
message: "Should have 3 groups",
|
|
});
|
|
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-group-header")).toEqual([
|
|
"Foo",
|
|
"Foo",
|
|
"Bar",
|
|
]);
|
|
|
|
expect(".o-map-renderer--pin-list-details").toHaveCount(3);
|
|
expect(".o-map-renderer--pin-list-details li").toHaveCount(5);
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details")).toEqual([
|
|
"FooProject\nBarProject\nFooBarProject",
|
|
"FooProject",
|
|
"BarProject",
|
|
]);
|
|
expect(".leaflet-marker-icon").toHaveCount(3);
|
|
});
|
|
|
|
test("Check groupBy on datetime field", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._fields["scheduled_date"] = fields.Datetime({
|
|
string: "Schedule date",
|
|
});
|
|
Task._records = [
|
|
{ id: 1, name: "FooProject", sequence: 1, partner_id: 1, scheduled_date: false },
|
|
];
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
searchViewId: false,
|
|
searchViewArch: `
|
|
<search>
|
|
<group expand='0' string='Group By'>
|
|
<filter string="scheduled_date" name="scheduled_date" context="{'group_by': 'scheduled_date'}"/>
|
|
</group>
|
|
</search>
|
|
`,
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(0, {
|
|
message: "Should not have any groups",
|
|
});
|
|
|
|
await toggleSearchBarMenu();
|
|
|
|
// don't throw an error when grouping a field with a false value
|
|
await toggleMenuItem("scheduled_date");
|
|
await toggleMenuItemOption("scheduled_date", "year");
|
|
});
|
|
|
|
test("Check groupBy on properties field", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Partner._fields["properties_definition"] = fields.PropertiesDefinition({
|
|
string: "Properties Definition",
|
|
});
|
|
Task._fields["task_properties"] = fields.Properties({
|
|
string: "Properties",
|
|
definition_record: "partner_id",
|
|
definition_record_field: "properties_definition",
|
|
});
|
|
Partner._records = [
|
|
{
|
|
id: 1,
|
|
name: "Bar",
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Foo",
|
|
partner_latitude: 10.0,
|
|
partner_longitude: 10.5,
|
|
sequence: 3,
|
|
properties_definition: [
|
|
{
|
|
name: "bd6404492c244cff",
|
|
type: "char",
|
|
string: "Reference Number",
|
|
},
|
|
],
|
|
},
|
|
];
|
|
Task._records = [
|
|
{
|
|
id: 1,
|
|
name: "FooProject",
|
|
sequence: 1,
|
|
partner_id: 1,
|
|
task_properties: {
|
|
bd6404492c244cff: "1234",
|
|
},
|
|
},
|
|
];
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
searchViewId: false,
|
|
searchViewArch: `
|
|
<search>
|
|
<group expand='0' string='Group By'>
|
|
<filter string="task_properties" name="task_properties"
|
|
context="{'group_by': 'task_properties'}"/>
|
|
</group>
|
|
</search>
|
|
`,
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(0, {
|
|
message: "Should not have any groups",
|
|
});
|
|
|
|
await toggleSearchBarMenu();
|
|
|
|
// don't throw an error when grouping on a property
|
|
await toggleMenuItem("task_properties");
|
|
await animationFrame();
|
|
await contains(".o_accordion_values .o_menu_item").click();
|
|
|
|
// check that the property has been added in the facet without crashing
|
|
expect(`.o_facet_value:contains("Reference Number")`).toHaveCount(1);
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(0);
|
|
});
|
|
|
|
test("Change groupBy", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
|
|
Task._records = TEST_RECORDS.task.threeRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
searchViewId: false,
|
|
searchViewArch: `
|
|
<search>
|
|
<filter string="Partner" name="partner_id" context="{'group_by': 'partner_id'}"/>
|
|
<filter string="Name" name="name" context="{'group_by': 'name'}"/>
|
|
</search>
|
|
`,
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(0, {
|
|
message: "Should not have any groups",
|
|
});
|
|
|
|
await toggleSearchBarMenu();
|
|
await toggleMenuItem("Partner");
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(2, {
|
|
message: "Should have 2 groups",
|
|
});
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-group-header")).toEqual(["Bar", "Foo"]);
|
|
// Groups should be loaded too
|
|
expect(".o-map-renderer--pin-list-details li").toHaveCount(3);
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details")).toEqual([
|
|
"FooProject\nFooBarProject",
|
|
"BarProject",
|
|
]);
|
|
|
|
await toggleMenuItem("Name");
|
|
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-group-header")).toEqual(["Bar", "Foo"], {
|
|
message: "Should not have changed",
|
|
});
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details")).toEqual([
|
|
"FooProject\nFooBarProject",
|
|
"BarProject",
|
|
]);
|
|
|
|
await toggleMenuItem("Partner");
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(3, {
|
|
message: "Should have 3 groups",
|
|
});
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-group-header")).toEqual([
|
|
"FooProject",
|
|
"BarProject",
|
|
"FooBarProject",
|
|
]);
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details")).toEqual([
|
|
"FooProject",
|
|
"BarProject",
|
|
"FooBarProject",
|
|
]);
|
|
expect(".o-map-renderer--pin-list-details:eq(0) li").toHaveCount(1);
|
|
expect(".o-map-renderer--pin-list-details:eq(1) li").toHaveCount(1);
|
|
expect(".o-map-renderer--pin-list-details:eq(2) li").toHaveCount(1);
|
|
});
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Controller testing
|
|
//--------------------------------------------------------------------------
|
|
|
|
test("Click on open button switches to form view", async () => {
|
|
mockService("action", {
|
|
switchView(name, props) {
|
|
expect.step("switchView");
|
|
expect(name).toBe("form", { message: "The view switched to should be form" });
|
|
expect(props).toEqual({ resId: 1 }, { message: "Props should be correct" });
|
|
},
|
|
});
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
|
|
await mountView({
|
|
config: { views: [[false, "form"]] },
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
expect(
|
|
"div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open"
|
|
).toHaveCount(1, { message: "The button should be present in the dom" });
|
|
await contains(
|
|
"div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open"
|
|
).click();
|
|
expect.verifySteps(["switchView"]);
|
|
});
|
|
|
|
test("Middle click on open button open record in tab", async () => {
|
|
mockService("action", {
|
|
switchView(name, props, options) {
|
|
expect.step("switchView");
|
|
expect(name).toBe("form", { message: "The view switched to should be form" });
|
|
expect(props).toEqual({ resId: 1 }, { message: "Props should be correct" });
|
|
expect(options).toEqual({ newWindow: true });
|
|
},
|
|
});
|
|
Task._views.form = "<form/>";
|
|
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
|
|
await mountView({
|
|
config: { views: [[false, "form"]] },
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
expect(
|
|
"div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open"
|
|
).toHaveCount(1, { message: "The button should be present in the dom" });
|
|
await contains(
|
|
"div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open"
|
|
).click({ ctrlKey: true });
|
|
expect.verifySteps(["switchView"]);
|
|
});
|
|
|
|
test("Test the lack of open button", async () => {
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id"></map>`,
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
|
|
expect(
|
|
"div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open"
|
|
).toHaveCount(0, { message: "The button should not be present in the dom" });
|
|
});
|
|
|
|
test("attribute panel_title on the arch should display in the pin list", async () => {
|
|
Task._records = TEST_RECORDS.task.oneRecord;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" panel_title="AAAAAAAAAAAAAAAAA"></map>`,
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-container .o_pin_list_header span").toHaveText(
|
|
"AAAAAAAAAAAAAAAAA"
|
|
);
|
|
});
|
|
|
|
test("Test using a field other than partner_id for the map view", async () => {
|
|
Task._records = TEST_RECORDS.task.anotherPartnerId;
|
|
Partner._records = TEST_RECORDS.partner.oneLocatedRecord;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="another_partner_id"></map>`,
|
|
});
|
|
|
|
await contains("div.leaflet-marker-icon").click();
|
|
|
|
expect(
|
|
"div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open"
|
|
).toHaveCount(0, { message: "The button should not be present in the dom" });
|
|
});
|
|
|
|
test("Check Google Maps URL is updating on domain change", async () => {
|
|
Task._records = [
|
|
{ id: 1, name: "FooProject", sequence: 1, partner_id: 2 },
|
|
{ id: 2, name: "BarProject", sequence: 2, partner_id: 3 },
|
|
];
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id"/>`,
|
|
searchViewArch: `
|
|
<search>
|
|
<filter name="some_filter" string="FooProject only" domain="[['name', '=', 'FooProject']]"/>
|
|
</search>`,
|
|
});
|
|
|
|
expect("a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&waypoints=Chauss%C3%A9e%20de%20Namur%2040%2C%201367%2C%20Ramillies|Chauss%C3%A9e%20de%20Wavre%2050%2C%201367%2C%20Ramillies",
|
|
{ message: "The link's URL initially should contain the addresses for all records" }
|
|
);
|
|
|
|
//apply domain and check that the Google Maps URL on the button reflects the changes
|
|
await toggleSearchBarMenu();
|
|
await toggleMenuItem("FooProject only");
|
|
expect("a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&waypoints=Chauss%C3%A9e%20de%20Namur%2040%2C%201367%2C%20Ramillies",
|
|
{
|
|
message:
|
|
"The link's URL after domain is applied should only contain addresses for filtered records",
|
|
}
|
|
);
|
|
});
|
|
|
|
test("Check Google Maps URL (routing and multiple records)", async () => {
|
|
Task._records = [
|
|
{ id: 1, name: "FooProject", sequence: 1, partner_id: 2 },
|
|
{ id: 2, name: "BarProject", sequence: 2, partner_id: 3 },
|
|
];
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1"/>`,
|
|
});
|
|
|
|
expect("a.btn.btn-primary").toHaveAttribute(
|
|
"href",
|
|
"https://www.google.com/maps/dir/?api=1&destination=Chauss%C3%A9e%20de%20Wavre%2050%2C%201367%2C%20Ramillies&waypoints=Chauss%C3%A9e%20de%20Namur%2040%2C%201367%2C%20Ramillies",
|
|
{ message: "The link's URL initially should contain the addresses for all records" }
|
|
);
|
|
});
|
|
|
|
test("Do not notify if unmounted after fetching coordinate", async () => {
|
|
const def = new Deferred();
|
|
|
|
patchWithCleanup(MapModel.prototype, {
|
|
_fetchCoordinatesFromAddressOSM() {
|
|
return def;
|
|
},
|
|
_notifyFetchedCoordinate() {
|
|
expect.step("notify");
|
|
},
|
|
});
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1"/>`,
|
|
});
|
|
const controller = getMapController(view);
|
|
|
|
destroy(controller);
|
|
|
|
def.resolve();
|
|
await animationFrame();
|
|
|
|
expect.verifySteps([]);
|
|
});
|
|
|
|
test("Do not fetch if unmounted after waiting interval", async () => {
|
|
patchWithCleanup(MapModel.prototype, {
|
|
async _fetchCoordinatesFromAddressOSM() {
|
|
expect.step("_fetchCoordinatesFromAddressOSM");
|
|
},
|
|
_notifyFetchedCoordinate() {
|
|
expect.step("_notifyFetchedCoordinate");
|
|
},
|
|
_openStreetMapAPIAsync() {
|
|
expect.step("_openStreetMapAPIAsync");
|
|
return super._openStreetMapAPIAsync(...arguments);
|
|
},
|
|
});
|
|
|
|
const view = await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1"/>`,
|
|
});
|
|
const controller = getMapController(view);
|
|
|
|
destroy(controller);
|
|
await animationFrame();
|
|
|
|
expect.verifySteps(["_openStreetMapAPIAsync"]);
|
|
});
|
|
|
|
test("Check groupBy on selection field", async () => {
|
|
expect.assertions(1);
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
Task._records = [
|
|
{ id: 1, name: "Project", sequence: 1, partner_id: 1, task_status: "abc" },
|
|
];
|
|
onRpc(
|
|
"res.partner",
|
|
"search_read",
|
|
() => TEST_RECORDS.partner.twoRecordsAddressCoordinates
|
|
);
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" />`,
|
|
groupBy: ["task_status"],
|
|
});
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(1, { message: "ABC" });
|
|
});
|
|
|
|
test("display '0' for false group, when grouped by int field", async () => {
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
searchViewId: false,
|
|
groupBy: ["sequence"],
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveText("0");
|
|
});
|
|
|
|
test("Map view with default_group_by", async () => {
|
|
Task._records = TEST_RECORDS.task.threeRecords;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map default_group_by="partner_id" res_partner="partner_id" />`,
|
|
});
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(2, {
|
|
message: "Should have 2 groups",
|
|
});
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-group-header")).toEqual(["Bar", "Foo"]);
|
|
expect(".o-map-renderer--pin-list-details").toHaveCount(2);
|
|
expect(".o-map-renderer--pin-list-details li").toHaveCount(3);
|
|
expect(queryAllTexts(".o-map-renderer--pin-list-details")).toEqual([
|
|
"FooProject\nFooBarProject",
|
|
"BarProject",
|
|
]);
|
|
});
|
|
|
|
test("GroupBy on datetime field with no subgroup specified", async () => {
|
|
patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
|
|
Task._records = TEST_RECORDS.task.twoRecordsFieldDateTime;
|
|
Partner._records = TEST_RECORDS.partner.twoRecordsAddressCoordinates;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map default_group_by="scheduled_date" res_partner="partner_id" />`,
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveCount(2);
|
|
expect(".o-map-renderer--pin-list-group-header:eq(1)").toHaveText("February 2022", {
|
|
message: "Should default to month scale when not specified",
|
|
});
|
|
});
|
|
|
|
test("display the field's falsy_value_label for false group, if defined", async () => {
|
|
Task._fields.partner_id.falsy_value_label = "I'm the false group";
|
|
Task._records[0].partner_id = false;
|
|
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id" routing="1" />`,
|
|
searchViewId: false,
|
|
groupBy: ["partner_id"],
|
|
});
|
|
|
|
expect(".o-map-renderer--pin-list-group-header").toHaveText("I'm the false group");
|
|
});
|
|
});
|
|
|
|
describe.tags("mobile");
|
|
describe("map_view_mobile", () => {
|
|
test("use pin list container on mobile", async () => {
|
|
await mountView({
|
|
type: "map",
|
|
resModel: "project.task",
|
|
arch: `<map res_partner="partner_id">
|
|
<field name="name" string="Project"/>
|
|
</map>`,
|
|
});
|
|
|
|
expect(".o_pin_list_header .fa.fa-caret-left").toHaveCount(1);
|
|
expect(".o_pin_list_header .fa.fa-caret-down").toHaveCount(0);
|
|
|
|
await contains(".o_pin_list_header").click();
|
|
expect(".o-sm-pin-list-container.h-100").toHaveCount(1, {
|
|
message: "There should extend the pin list container",
|
|
});
|
|
expect(".o_pin_list_header .fa.fa-caret-down").toHaveCount(1);
|
|
expect(".o_pin_list_header .fa.fa-caret-left").toHaveCount(0);
|
|
|
|
await contains(".o_pin_list_header").click();
|
|
expect(".o_pin_list_header .fa.fa-caret-left").toHaveCount(1);
|
|
expect(".o_pin_list_header .fa.fa-caret-down").toHaveCount(0);
|
|
expect(".o-sm-pin-list-container.h-100").toHaveCount(0, {
|
|
message: "There should collapse the pin list container",
|
|
});
|
|
});
|
|
});
|