/** @odoo-module **/

import { MapModel } from "@web_map/map_view/map_model";
import { makeView } from "@web/../tests/views/helpers";
import {
    setupControlPanelServiceRegistry,
    toggleFilterMenu,
    toggleMenuItem,
    toggleGroupByMenu,
    toggleMenuItemOption,
} from "@web/../tests/search/helpers";
import { registry } from "@web/core/registry";
import { dialogService } from "@web/core/dialog/dialog_service";
import {
    click,
    getFixture,
    makeDeferred,
    nextTick,
    patchWithCleanup,
    destroy,
    findChildren,
} from "@web/../tests/helpers/utils";
import { session } from "@web/session";
import { browser } from "@web/core/browser/browser";
import {
    makeFakeHTTPService,
    makeFakeLocalizationService,
} from "@web/../tests/helpers/mock_services";
import { MapRenderer } from "@web_map/map_view/map_renderer";

const serviceRegistry = registry.category("services");

let serverData;
let target;
const MAP_BOX_TOKEN = "token";

function findMapRenderer(map) {
    return findChildren(map, (c) => c.component instanceof MapRenderer).component;
}

QUnit.module("Views", (hooks) => {
    hooks.beforeEach(() => {
        target = getFixture();
        const models = {
            "project.task": {
                fields: {
                    display_name: { string: "name", type: "char" },
                    sequence: { string: "sequence", type: "integer" },
                    partner_id: {
                        string: "partner",
                        type: "many2one",
                        relation: "res.partner",
                    },
                    another_partner_id: {
                        string: "another relation",
                        type: "many2one",
                        relation: "res.partner}",
                    },
                    partner_ids: {
                        string: "Partners",
                        type: "one2many",
                        comodel_name: "res.partner",
                        relation: "res.partner",
                        relation_field: "task_id",
                    },
                },
                records: [{ id: 1, display_name: "project", partner_id: 1 }],
                oneRecord: {
                    records: [{ id: 1, display_name: "Foo", partner_id: [1] }],
                    length: 1,
                },

                twoRecords: {
                    records: [
                        { id: 1, display_name: "FooProject", sequence: 1, partner_id: [1] },
                        { id: 2, display_name: "BarProject", sequence: 2, partner_id: [2] },
                    ],
                    length: 2,
                },

                threeRecords: {
                    records: [
                        {
                            id: 1,
                            display_name: "FooProject",
                            sequence: 1,
                            partner_id: [1],
                            partner_ids: [1, 2],
                        },
                        {
                            id: 2,
                            display_name: "BarProject",
                            sequence: 2,
                            partner_id: [2],
                            partner_ids: [1, 3],
                        },
                        {
                            id: 3,
                            display_name: "FooBarProject",
                            sequence: 3,
                            partner_id: [1],
                            partner_ids: [1],
                        },
                    ],
                    length: 3,
                },

                twoRecordOnePartner: {
                    records: [
                        { id: 1, display_name: "FooProject", partner_id: [1] },
                        { id: 2, display_name: "BarProject", partner_id: [1] },
                    ],
                    length: 2,
                },
                noRecord: {
                    records: [],
                    length: 0,
                },
                recordWithouthPartner: {
                    records: [{ id: 1, display_name: "Foo", partner_id: [] }],
                    length: 1,
                },
                anotherPartnerId: {
                    records: [{ id: 1, display_name: "FooProject", another_partner_id: [1] }],
                    length: 1,
                },
            },
            "res.partner": {
                fields: {
                    name: { string: "Customer", type: "char" },
                    partner_latitude: { string: "Latitude", type: "float" },
                    partner_longitude: { string: "Longitude", type: "float" },
                    contact_address_complete: { string: "Address", type: "char" },
                    task_ids: {
                        string: "Task",
                        type: "one2many",
                        relation: "project.task",
                        relation_field: "partner_id",
                    },
                    sequence: { string: "sequence", type: "integer" },
                },
                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,
                    },
                    {
                        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: 3,
                        name: "Bar",
                        partner_latitude: 11.0,
                        partner_longitude: 11.5,
                        contact_address_complete: "Chaussée de Wavre 50, 1367, Ramillies",
                        sequence: 4,
                    },
                ],
                methods: {
                    update_latitude_longitude() {
                        return Promise.resolve();
                    },
                },
                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",
                    },
                ],
                emptyRecords: [],
                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",
                    },
                ],
                unlocatedRecords: [{ id: 1, name: "Foo" }],
                noCoordinatesWrongAddress: [
                    {
                        id: 1,
                        name: "Foo",
                        contact_address_complete: "Cfezfezfefes",
                    },
                ],
            },
        };
        serverData = { models };
        setupControlPanelServiceRegistry();
        serviceRegistry.add("dialog", dialogService);
        serviceRegistry.add("localization", makeFakeLocalizationService());
        serviceRegistry.add("http", makeFakeHTTPService());

        patchWithCleanup(browser, {
            setTimeout: (fn) => fn(),
            clearTimeout: () => {},
        });

        patchWithCleanup(MapModel, {
            // set delay to 0 as _fetchCoordinatesFromAddressOSM is mocked
            COORDINATE_FETCH_DELAY: 0,
        });
        patchWithCleanup(MapModel.prototype, {
            _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 Promise.resolve(failResponse);
                    case "":
                        return Promise.resolve(failResponse);
                }
                return Promise.resolve(successResponse);
            },
            _fetchCoordinatesFromAddressOSM(metaData, data, record) {
                const coordinates = [];
                coordinates[0] = { lat: 10.0, lon: 10.5 };
                switch (record.contact_address_complete) {
                    case "Cfezfezfefes":
                        return Promise.resolve([]);
                    case "":
                        return Promise.resolve([]);
                }
                return Promise.resolve(coordinates);
            },
            _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 Promise.resolve({ 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);
            },
        });
    });

    QUnit.module("MapView");

    //--------------------------------------------------------------------------
    // Testing data fetching
    //--------------------------------------------------------------------------

    /**
     * data: no record
     * Should have no record
     * Should have no marker
     * Should have no route
     */
    QUnit.test("Create a view with no record", async function (assert) {
        assert.expect(8);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `
                    <map res_partner="partner_id" routing="1">
                        <field name="name" string="Project"/>
                    </map>
                `,
            async mockRPC(route, { model, kwargs }) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        assert.strictEqual(
                            model,
                            "project.task",
                            "The model should be project.task"
                        );
                        assert.strictEqual(kwargs.fields[0], "partner_id");
                        assert.strictEqual(kwargs.fields[1], "display_name");
                        return serverData.models["project.task"].noRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        assert.ok(
                            false,
                            "Should not search_read the partners if there are no partner"
                        );
                }
            },
        });
        assert.strictEqual(
            target.querySelector("a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1",
            "The link's URL should not contain any coordinates"
        );
        assert.strictEqual(
            map.model.metaData.resPartnerField,
            "partner_id",
            "the resPartnerField should be set"
        );
        assert.strictEqual(map.model.data.records.length, 0, "There should be no records");
        assert.containsNone(target, "div.leaflet-marker-icon", "No marker should be on a the map.");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "No route should be shown"
        );
    });

    /**
     * data: one record that has no partner linked to it
     * The record should be kept and displayed in the list of records in gray (no clickable)
     * should have no marker
     * Should have no route
     */
    QUnit.test("Create a view with one record that has no partner", async function (assert) {
        assert.expect(5);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].recordWithouthPartner;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].emptyRecords;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 1, "There should be 1 records");
        assert.containsNone(target, "div.leaflet-marker-icon", "No marker should be on a the map.");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "No route should be shown"
        );
        assert.containsOnce(
            target,
            ".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
        );
        assert.containsOnce(
            target,
            ".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li span"
        );
    });

    /**
     * data: one record that has a partner which has coordinates but no address
     * One record
     * One marker
     * no route
     */
    QUnit.test(
        "Create a view with one record and a partner located by coordinates",
        async function (assert) {
            assert.expect(3);
            patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
            const map = await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" routing="1" />`,
                async mockRPC(route) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].oneRecord;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].coordinatesNoAddress;
                    }
                },
            });
            assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
            assert.containsOnce(
                target,
                "div.leaflet-marker-icon",
                "There should be one marker on the map"
            );
            assert.containsNone(
                target.querySelector(".leaflet-overlay-pane"),
                "path",
                "There should be no route on the map"
            );
        }
    );

    /**
     * data: one record linked to one partner with no address and wrong coordinates
     * api: MapBox
     * record should be kept and displayed in the list
     * no route
     * no marker
     */
    QUnit.test(
        "Create view with one record linked to a partner with wrong coordinates with MB",
        async function (assert) {
            assert.expect(5);
            patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
            const map = await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" routing="1" />`,
                async mockRPC(route) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].oneRecord;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].wrongCoordinatesNoAddress;
                    }
                },
            });
            assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
            assert.containsNone(
                target,
                "div.leaflet-marker-icon",
                "There should be np marker on the map"
            );
            assert.containsNone(
                target.querySelector(".leaflet-overlay-pane"),
                "path",
                "There should be no route on the map"
            );
            assert.containsOnce(
                target,
                ".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li"
            );
            assert.containsOnce(
                target,
                ".o-map-renderer--pin-list-container .o-map-renderer--pin-list-details li span"
            );
        }
    );

    /**
     * data: one record linked to one partner with no address and wrong coordinates
     * api: OpenStreet Map
     * record should be kept
     * no route
     * no marker
     */
    QUnit.test(
        "Create view with one record linked to a partner with wrong coordinates with OSM",
        async function (assert) {
            assert.expect(3);
            const map = await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" routing="1" />`,
                async mockRPC(route) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].oneRecord;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].wrongCoordinatesNoAddress;
                    }
                },
            });
            assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
            assert.containsNone(
                target,
                "div.leaflet-marker-icon",
                "There should be no marker on the map"
            );
            assert.containsNone(
                target.querySelector(".leaflet-overlay-pane"),
                "path",
                "There should be no route on the map"
            );
        }
    );
    /**
     * 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
     */
    QUnit.test(
        "Create View with one record linked to a partner with no coordinates and right address OSM",
        async function (assert) {
            assert.expect(7);
            const map = await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" routing="1" />`,
                async mockRPC(route, { args, method, model }) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].oneRecord;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].noCoordinatesGoodAddress;
                        case "/web/dataset/call_kw/res.partner/update_latitude_longitude":
                            assert.strictEqual(
                                model,
                                "res.partner",
                                'The model should be "res.partner"'
                            );
                            assert.strictEqual(method, "update_latitude_longitude");
                            assert.strictEqual(
                                args[0].length,
                                1,
                                "There should be one record needing caching"
                            );
                            assert.strictEqual(args[0][0].id, 1, "The records's id should be 1");
                    }
                },
            });
            assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
            assert.containsOnce(
                target,
                "div.leaflet-marker-icon",
                "There should be one marker on the map"
            );
            assert.containsNone(
                target.querySelector(".leaflet-overlay-pane"),
                "path",
                "There should be no route on the map"
            );
        }
    );

    /**
     * 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
     */
    QUnit.test(
        "Create View with one record linked to a partner with no coordinates and right address MB",
        async function (assert) {
            assert.expect(7);
            patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
            const map = await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" routing="1" />`,
                async mockRPC(route, { args, method, model }) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].oneRecord;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].noCoordinatesGoodAddress;
                        case "/web/dataset/call_kw/res.partner/update_latitude_longitude":
                            assert.strictEqual(
                                model,
                                "res.partner",
                                'The model should be "res.partner"'
                            );
                            assert.strictEqual(method, "update_latitude_longitude");
                            assert.strictEqual(
                                args[0].length,
                                1,
                                "There should be one record needing caching"
                            );
                            assert.strictEqual(args[0][0].id, 1, "The records's id should be 1");
                    }
                },
            });
            assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
            assert.containsOnce(
                target,
                "div.leaflet-marker-icon",
                "There should be one marker on the map"
            );
            assert.containsNone(
                target.querySelector(".leaflet-overlay-pane"),
                "path",
                "There should be no route on the map"
            );
        }
    );

    /**
     * data: one record linked to a partner with no coordinates and no address
     * api: MapBox
     * 1 record
     * no route
     * no marker
     */
    QUnit.test("Create view with no located record", async function (assert) {
        assert.expect(3);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].unlocatedRecords;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
        assert.containsNone(target, "div.leaflet-marker-icon", "No marker should be on a the map.");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "No route should be shown"
        );
    });

    /**
     * data: one record linked to a partner with no coordinates and no address
     * api: OSM
     * one record
     * no route
     * no marker
     */
    QUnit.test("Create view with no located record OSM", async function (assert) {
        assert.expect(3);
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].unlocatedRecords;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
        assert.containsNone(target, "div.leaflet-marker-icon", "No marker should be on a the map.");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "No route should be shown"
        );
    });

    /**
     * data: one record linked to a partner with no coordinates and wrong address
     * api: OSM
     * one record
     * no route
     * no marker
     */
    QUnit.test("Create view with no badly located record OSM", async function (assert) {
        assert.expect(3);
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].noCoordinatesWrongAddress;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
        assert.containsNone(target, "div.leaflet-marker-icon", "No marker should be on a the map.");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "No route should be shown"
        );
    });

    /**
     * data: one record linked to a partner with no coordinates and wrong address
     * api: mapbox
     * one record
     * no route
     * no marker
     */

    QUnit.test("Create view with no badly located record MB", async function (assert) {
        assert.expect(3);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].noCoordinatesWrongAddress;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 1, "There should be one records");
        assert.containsNone(target, "div.leaflet-marker-icon", "No marker should be on a the map.");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "No route should be shown"
        );
    });

    /**
     * data: 2 records linked to the same partner
     * 2 records
     * 2 markers
     * no route
     * same partner object
     * 1 caching request
     */
    QUnit.test("Create a view with two located records same partner", async function (assert) {
        assert.expect(4);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecordOnePartner;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].oneLocatedRecord;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 2, "There should be 2 records");
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );
        assert.containsOnce(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "There should be one route showing"
        );
        assert.equal(
            map.model.data.records[0].partner,
            map.model.data.records[1].partner,
            "The records should have the same partner object as a property"
        );
    });

    /**
     * data: 2 records linked to differnet partners
     * 2 records
     * 1 route
     * different partner object.
     * 2 caching
     */
    QUnit.test(
        "Create a a view with two located records different partner",
        async function (assert) {
            assert.expect(5);
            patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
            const map = await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" routing="1" />`,
                async mockRPC(route, { args }) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].twoRecords;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                        case "/web/dataset/call_kw/res.partner/update_latitude_longitude":
                            assert.strictEqual(
                                args[0].length,
                                2,
                                "Should have 2 record needing caching"
                            );
                    }
                },
            });
            assert.strictEqual(map.model.data.records.length, 2, "There should be 2 records");
            assert.strictEqual(
                target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                    .textContent,
                "2",
                "There should be a marker for two records"
            );
            assert.containsOnce(
                target.querySelector(".leaflet-overlay-pane"),
                "path",
                "There should be one route showing"
            );
            assert.notEqual(
                map.model.data.records[0].partner,
                map.model.data.records[1].partner,
                "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
     */
    QUnit.test("Create a view with res.partner", async function (assert) {
        assert.expect(8);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        serverData.models["res.partner"].recordsPrimary = {
            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,
                },
            ],
            length: 2,
        };
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "res.partner",
            arch: `<map res_partner="id" />`,
            async mockRPC(route, { kwargs, model }) {
                switch (route) {
                    case "/web/dataset/call_kw/res.partner/web_search_read":
                        assert.strictEqual(model, "res.partner", "The model should be res.partner");
                        assert.strictEqual(kwargs.fields[0], "id");
                        return serverData.models["res.partner"].recordsPrimary;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        assert.strictEqual(
                            model,
                            "res.partner",
                            "The model should be res.partner as well"
                        );
                        assert.strictEqual(kwargs.domain[1][2][0], 2);
                        assert.strictEqual(kwargs.domain[1][2][1], 1);
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 2, "There should be two records");
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "There should be no route showing"
        );
    });

    /**
     * data: 3 records linked to one located partner and one unlocated
     * test if only the 2 located records are displayed
     */
    QUnit.test("Create a view with 2 located records and 1 unlocated", async function (assert) {
        assert.expect(4);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].threeRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsOneUnlocated;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 3);
        assert.strictEqual(map.model.data.records[0].partner.id, 1, "The partner's id should be 1");
        assert.strictEqual(map.model.data.records[1].partner.id, 2, "The partner's id should be 2");
        assert.strictEqual(map.model.data.records[2].partner.id, 1, "The partner's id should be 1");
    });

    QUnit.test("Change load limit", async function (assert) {
        assert.expect(2);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        serverData.models["project.task"].records =
            serverData.models["project.task"].threeRecords.records;
        serverData.models["res.partner"].records =
            serverData.models["res.partner"].twoRecordsAddressCoordinates;
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" limit="2" />`,
        });
        assert.strictEqual(
            target.querySelector(`.o_pager_counter .o_pager_value`).textContent.trim(),
            "1-2"
        );
        assert.strictEqual(
            target.querySelector(`.o_pager_counter span.o_pager_limit`).innerText.trim(),
            "3"
        );
    });

    //--------------------------------------------------------------------------
    // Renderer testing
    //--------------------------------------------------------------------------

    QUnit.test("Google Maps redirection", async function (assert) {
        assert.expect(2);

        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id"></map>`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });

        assert.strictEqual(
            target.querySelector("a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&waypoints=10,10.5",
            "The link's URL should contain the right sets of coordinates"
        );

        await click(target, ".leaflet-marker-icon");
        assert.strictEqual(
            target.querySelector("div.leaflet-popup a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&destination=10,10.5",
            "The link's URL should the right set of coordinates"
        );
    });

    QUnit.test("Google Maps redirection (with routing = true)", async function (assert) {
        assert.expect(2);

        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1"></map>`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });

        assert.strictEqual(
            target.querySelector("a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&destination=10,10.5",
            "The link's URL should contain the right sets of coordinates"
        );

        await click(target, ".leaflet-marker-icon");
        assert.strictEqual(
            target.querySelector("div.leaflet-popup a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&destination=10,10.5",
            "The link's URL should the right set of coordinates"
        );
    });

    QUnit.test("Unicity of coordinates in Google Maps url", async function (assert) {
        assert.expect(2);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecordOnePartner;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        assert.strictEqual(
            target.querySelector("a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&waypoints=10.5,10",
            "The link's URL should contain unqiue sets of coordinates"
        );
        await click(target, ".leaflet-marker-icon");
        assert.strictEqual(
            target.querySelector("div.leaflet-popup a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&destination=10.5,10",
            "The link's URL should only contain unqiue sets of coordinates"
        );
    });

    QUnit.test("test the position of pin", async function (assert) {
        assert.expect(4);
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });

        assert.containsOnce(target, ".o-map-renderer--marker", "Should have one marker created");
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );
        const renderer = findMapRenderer(map);
        assert.strictEqual(
            renderer.markers[0].getLatLng().lat,
            10,
            "The latitude should be the same as the record"
        );
        assert.strictEqual(
            renderer.markers[0].getLatLng().lng,
            10.5,
            "The longitude should be the same as the record"
        );
    });

    /**
     * data: two located records
     * Create an empty map
     */
    QUnit.test("Create of a empty map", async function (assert) {
        assert.expect(6);

        const map = await makeView({
            serverData,
            type: "map",
            resModel: "res.partner",
            arch: `<map />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        assert.notOk(map.model.metaData.resPartnerField, "the resPartnerField should not be set");

        assert.hasClass(target.querySelector(".o_map_view"), "o_view_controller");
        assert.containsOnce(target, ".leaflet-map-pane", "If the map exists this div should exist");
        assert.ok(
            target.querySelector(".leaflet-pane .leaflet-tile-pane").children.length,
            "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
        assert.hasClass(
            target.querySelector(".o-map-renderer--container"),
            "leaflet-container",
            "the map should be in the DOM"
        );

        assert.strictEqual(
            target.querySelector(".leaflet-overlay-pane").children.length,
            0,
            "Should have no showing route"
        );
    });

    /**
     * two located records
     * without routing or default_order
     * normal marker icon
     * test the click on them
     */
    QUnit.test("Create view with normal marker icons", async function (assert) {
        assert.expect(6);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        assert.notOk(map.model.metaData.numbering, "the numbering option should not be enabled");
        assert.notOk(map.model.metaData.routing, "The routing option should not be enabled");

        assert.containsOnce(target, ".leaflet-marker-icon", "There should be 1 marker");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "There should be no route showing"
        );

        await click(target, ".leaflet-marker-icon");

        assert.strictEqual(
            target.querySelector(".leaflet-popup-pane").children.length,
            1,
            "Should have one showing popup"
        );

        await click(target, "div.leaflet-container");
        // wait for the popup's destruction which takes a certain time...
        for (let i = 0; i < 15; i++) {
            await nextTick();
        }

        assert.strictEqual(
            target.querySelector(".leaflet-popup-pane").children.length,
            0,
            "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
     */
    QUnit.test("Create a view with default_order", async function (assert) {
        assert.expect(7);
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" default_order="name" />`,
            async mockRPC(route, { kwargs }) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        assert.deepEqual(
                            kwargs.order,
                            "name ASC",
                            "The sorting order should be on the field name in a ascendant way"
                        );
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        assert.notOk(map.model.metaData.numbering, "the numbering option should not be enabled");
        assert.notOk(map.model.metaData.routing, "The routing option should not be enabled");
        assert.containsOnce(target, "div.leaflet-marker-icon", "There should be 1 marker");
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );
        assert.strictEqual(
            target.querySelector(".leaflet-popup-pane").children.length,
            0,
            "Should have no showing popup"
        );
        await click(target, "div.leaflet-marker-icon");
        assert.strictEqual(
            target.querySelector(".leaflet-popup-pane").children.length,
            1,
            "Should have one showing popup"
        );
    });

    /**
     * two locted records
     * with routing enabled
     * numbered icon
     * test click on route
     */
    QUnit.test("Create a view with routing", async function (assert) {
        assert.expect(9);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });

        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        assert.ok(map.model.metaData.numbering, "The numbering option should be enabled");
        assert.ok(map.model.metaData.routing, "The routing option should be enabled");

        assert.strictEqual(
            map.model.data.numberOfLocatedRecords,
            2,
            "Should have 2 located Records"
        );
        assert.strictEqual(map.model.data.routes.length, 1, "Should have 1 computed route");
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );
        assert.strictEqual(
            target.querySelector("path.leaflet-interactive").getAttribute("stroke"),
            "blue",
            "The route should be blue if it has not been clicked"
        );
        assert.strictEqual(
            target.querySelector("path.leaflet-interactive").getAttribute("stroke-opacity"),
            "0.3",
            "The opacity of the polyline should be 0.3"
        );
        // bypass the click helper because the element isn't visible
        target
            .querySelector("path.leaflet-interactive")
            .dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
        await nextTick();
        assert.strictEqual(
            target.querySelector("path.leaflet-interactive").getAttribute("stroke"),
            "darkblue",
            "The route should be darkblue after being clicked"
        );
        assert.strictEqual(
            target.querySelector("path.leaflet-interactive").getAttribute("stroke-opacity"),
            "1",
            "The opacity of the polyline should be 1"
        );
    });

    QUnit.test("Create a view with routingError", async function (assert) {
        assert.expect(1);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        patchWithCleanup(MapModel.prototype, {
            async _maxBoxAPI(metaData, data) {
                data.routingError = "this is test warning";
                data.routes = [];
            },
        });

        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return [];
                }
            },
        });

        assert.containsOnce(
            target,
            ".o-map-renderer > .o-map-renderer--alert",
            "should have alert"
        );
    });

    /**
     * routing with token and one located record
     * No route
     */
    QUnit.test("create a view with routing and one located record", async function (assert) {
        assert.expect(2);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].oneLocatedRecord;
                }
            },
        });
        assert.ok(map.model.metaData.routing, "The routing option should be enabled");
        assert.strictEqual(map.model.data.routes.length, 0, "Should have no computed route");
    });

    /**
     * no mapbox token
     * assert that the view uses the right api and routes
     */
    QUnit.test("CreateView with empty mapbox token setting", async function (assert) {
        assert.expect(2);

        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].recordWithouthPartner;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].emptyRecords;
                }
            },
        });
        assert.strictEqual(
            map.model.metaData.mapBoxToken,
            "",
            "The token should be an empty string"
        );
        assert.notOk(map.model.data.useMapBoxAPI, "model should not use mapbox");
    });

    /**
     * wrong mapbox token
     * assert that the view uses the openstreetmap api
     */
    QUnit.test("Create a view with wrong map box setting", async function (assert) {
        assert.expect(2);
        patchWithCleanup(session, { map_box_token: "vrve" });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        assert.strictEqual(map.model.metaData.mapBoxToken, "vrve", "The token should be kept");
        assert.notOk(map.model.data.useMapBoxAPI, "model should not use mapbox");
    });

    /**
     * wrong mapbox token fails at catch at route computing
     */
    QUnit.test(
        "create a view with wrong map box setting and located records",
        async function (assert) {
            assert.expect(2);
            patchWithCleanup(session, { map_box_token: "frezfre" });
            const map = await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" routing="1" />`,
                async mockRPC(route) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].twoRecords;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].twoRecordsAddressCoordinates;
                    }
                },
            });
            assert.strictEqual(
                map.model.metaData.mapBoxToken,
                "frezfre",
                "The token should be kept"
            );
            assert.notOk(map.model.data.useMapBoxAPI, "model should not use mapbox");
        }
    );

    /**
     * create view with right map box token
     * assert that the view uses the map box api
     */
    QUnit.test("Create a view with the right map box token", async function (assert) {
        assert.expect(2);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].recordWithouthPartner;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].emptyRecords;
                }
            },
        });
        assert.strictEqual(
            map.model.metaData.mapBoxToken,
            "token",
            "The token should be the right token"
        );
        assert.ok(map.model.data.useMapBoxAPI, "model should use mapbox");
    });

    /**
     * data: two located records
     */
    QUnit.test(
        "Click on pin shows popup, click on another shuts the first and open the other",
        async function (assert) {
            assert.expect(3);
            patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
            await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" routing="1" />`,
                async mockRPC(route) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].twoRecords;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                    }
                },
            });
            assert.notOk(
                target.querySelector(".leaflet-pane .leaflet-popup-pane").children.length,
                "The popup div should be empty"
            );

            await click(target, "div.leaflet-marker-icon");
            assert.strictEqual(
                target.querySelector(".leaflet-popup-pane").children.length,
                1,
                "The popup div should contain one element"
            );

            // bypass the click helper because the element isn't visible
            target
                .querySelector(".leaflet-map-pane")
                .dispatchEvent(new MouseEvent("click", { bubbles: true }));
            await nextTick();
            // wait for the popup's destruction which takes a certain time...
            for (let i = 0; i < 15; i++) {
                await nextTick();
            }
            assert.notOk(
                target.querySelector(".leaflet-pane .leaflet-popup-pane").children.length,
                "The popup div should be empty"
            );
        }
    );

    /**
     * data: two located records
     * asserts that all the records are shown on the map
     */
    QUnit.test("assert that all the records are shown on the map", async function (assert) {
        assert.expect(3);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        const mapX = target.querySelector(".leaflet-map-pane")._leaflet_pos.x;
        const mapY = target.querySelector(".leaflet-map-pane")._leaflet_pos.y;
        assert.ok(
            mapX - target.querySelector("div.leaflet-marker-icon")._leaflet_pos.x < 0,
            "If the marker is currently shown on the map, the subtraction of latitude should be under 0"
        );
        assert.ok(mapY - target.querySelector("div.leaflet-marker-icon")._leaflet_pos.y < 0);
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );
    });

    /**
     * data: two located records
     * asserts that the right fields are shown in the popup
     */
    QUnit.test("Content of the marker popup with one field", async function (assert) {
        assert.expect(5);
        serverData.views = {
            "project.task,false,form": "<form/>",
        };
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const map = await makeView({
            config: { views: [[false, "form"]] },
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `
                <map res_partner="partner_id" routing="1" hide_name="1" hide_address="1">
                    <field name="display_name" string="Name" />
                </map>
            `,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressCoordinates;
                }
            },
        });
        assert.strictEqual(map.model.metaData.fieldNamesMarkerPopup[0].fieldName, "display_name");

        await click(target, "div.leaflet-marker-icon");

        assert.strictEqual(
            map.model.metaData.fieldNamesMarkerPopup.length,
            1,
            "fieldsMarkerPopup should contain one field"
        );
        assert.containsOnce(target, "tbody tr", "The popup should have one field");
        assert.strictEqual(
            target.querySelector("tbody tr").textContent,
            "NameFoo",
            "Field row's text should be 'Name Foo'"
        );
        assert.strictEqual(
            target.querySelector(".o-map-renderer--popup-buttons").children.length,
            3,
            "The popup should contain 2 buttons and one divider"
        );
    });

    /**
     * data: two located records
     * asserts that no field is shown in popup
     */
    QUnit.test("Content of the marker with no field", async function (assert) {
        assert.expect(2);
        serverData.views = {
            "project.task,false,form": "<form/>",
        };
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        await makeView({
            config: { views: [[false, "form"]] },
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" hide_name="1" hide_address="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressNoCoordinates;
                }
            },
        });
        await click(target, "div.leaflet-marker-icon");

        assert.strictEqual(
            target.querySelector("tbody").children.length,
            0,
            "The popup should have only the button"
        );
        assert.strictEqual(
            target.querySelector(".o-map-renderer--popup-buttons").children.length,
            3,
            "The popup should contain 2 buttons and one divider"
        );
    });

    QUnit.test("Attribute: hide_name", async function (assert) {
        assert.expect(2);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" hide_name="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressCoordinates;
                }
            },
        });

        await click(target, "div.leaflet-marker-icon");

        assert.containsOnce(target, "tbody > tr", "The popup should have one field");
        assert.strictEqual(
            target
                .querySelector("tbody tr .o-map-renderer--popup-table-content-name")
                .textContent.trim(),
            "Address",
            "The popup should have address field"
        );
    });

    QUnit.test("Render partner address field in popup", async function (assert) {
        assert.expect(3);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" hide_name="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].oneLocatedRecord;
                }
            },
        });

        await click(target, "div.leaflet-marker-icon");

        assert.containsOnce(target, "tbody tr", "The popup should have one field");
        assert.strictEqual(
            target
                .querySelector("tbody tr .o-map-renderer--popup-table-content-name")
                .textContent.trim(),
            "Address",
            "The popup should have address field"
        );
        assert.strictEqual(
            target
                .querySelector("tbody tr .o-map-renderer--popup-table-content-value")
                .textContent.trim(),
            "Chaussée de Namur 40, 1367, Ramillies",
            "The popup should have correct address"
        );
    });

    QUnit.test("Hide partner address field in popup", async function (assert) {
        assert.expect(3);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" hide_address="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].oneLocatedRecord;
                }
            },
        });

        await click(target, "div.leaflet-marker-icon");

        assert.containsOnce(target, "tbody tr", "The popup should have one field");
        assert.strictEqual(
            target
                .querySelector("tbody tr .o-map-renderer--popup-table-content-name")
                .textContent.trim(),
            "Name",
            "The popup should have name field"
        );
        assert.strictEqual(
            target
                .querySelector("tbody tr .o-map-renderer--popup-table-content-value")
                .textContent.trim(),
            "Foo",
            "The popup should have correct address"
        );
    });

    QUnit.test("Handle records of same co-ordinates in marker", async function (assert) {
        assert.expect(4);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].twoRecords;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressCoordinates;
                }
            },
        });

        assert.containsOnce(target, "div.leaflet-marker-icon", "There should be a one marker");
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );

        await click(target, "div.leaflet-marker-icon");

        assert.containsOnce(target, "tbody tr", "The popup should have one field");
        assert.strictEqual(
            target
                .querySelector("tbody tr .o-map-renderer--popup-table-content-name")
                .textContent.trim(),
            "Address",
            "The popup should have address field"
        );
    });

    QUnit.test("Pager", async function (assert) {
        assert.expect(4);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return {
                            length: 101,
                            records: Array.from({ length: 101 }, (_, index) => {
                                return {
                                    id: index,
                                    name: "project",
                                    partner_id: [index],
                                };
                            }),
                        };
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return Array.from({ length: 101 }, (_, index) => ({
                            id: index,
                            name: "Foo",
                            partner_latitude: 10.0,
                            partner_longitude: 10.5,
                        }));
                }
            },
        });
        assert.containsOnce(target, ".o_pager");
        assert.strictEqual(
            target.querySelector(`.o_pager_counter .o_pager_value`).textContent.trim(),
            "1-80",
            "current pager value should be 1-20"
        );
        assert.strictEqual(
            target.querySelector(`.o_pager_counter span.o_pager_limit`).innerText.trim(),
            "101",
            "current pager limit should be 21"
        );

        await click(target.querySelector(`.o_pager button.o_pager_next`));

        assert.strictEqual(
            target.querySelector(`.o_pager_counter .o_pager_value`).textContent.trim(),
            "81-101",
            "pager value should be 21-40"
        );
    });

    QUnit.test("New domain", async function (assert) {
        assert.expect(13);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        serverData.models["project.task"].records = [
            { id: 1, name: "FooProject", sequence: 1, partner_id: 1 },
            { id: 2, name: "BarProject", sequence: 2, partner_id: 2 },
        ];
        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressCoordinates;
                }
            },
        });
        assert.strictEqual(map.model.data.records.length, 2, "There should be 2 records");
        assert.containsOnce(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "There should be one route displayed"
        );
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );

        map.env.searchModel.setDomainParts({
            test: {
                domain: [["name", "=", "FooProject"]],
            },
        });
        await nextTick();

        assert.strictEqual(map.model.data.records.length, 1, "There should be 1 record");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "There should be no route on the map"
        );
        assert.containsOnce(
            target,
            "div.leaflet-marker-icon",
            "There should be 1 marker on the map"
        );

        map.env.searchModel.setDomainParts({
            test: {
                domain: [["name", "=", "Foofezfezf"]],
            },
        });
        await nextTick();

        assert.strictEqual(map.model.data.records.length, 0, "There should be no record");
        assert.containsNone(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "There should be no route on the map"
        );
        assert.containsNone(
            target,
            "div.leaflet-marker-icon",
            "There should be 0 marker on the map"
        );

        map.env.searchModel.setDomainParts({
            test: {
                domain: [["name", "like", "Project"]],
            },
        });
        await nextTick();

        assert.strictEqual(map.model.data.records.length, 2, "There should be 2 record");
        assert.containsOnce(
            target.querySelector(".leaflet-overlay-pane"),
            "path",
            "There should be 1 route on the map"
        );
        assert.containsOnce(
            target,
            "div.leaflet-marker-icon",
            "There should be 1 marker on the map"
        );
        assert.strictEqual(
            target.querySelector("div.leaflet-marker-icon .o-map-renderer--marker-badge")
                .textContent,
            "2",
            "There should be a marker for two records"
        );
    });

    QUnit.test("Toggle grouped pin lists", async function (assert) {
        assert.expect(13);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const records = serverData.models["project.task"].threeRecords;
        const partners = serverData.models["res.partner"].twoRecordsAddressCoordinates;
        for (const record of records.records) {
            // add name on partner_id to have name_get like value
            record.partner_id.push(partners.find((x) => x.id === record.partner_id[0]).name);
        }
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return records;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return partners;
                }
            },
            groupBy: ["partner_id"],
        });

        assert.containsN(
            target,
            ".o-map-renderer--pin-list-group-header",
            2,
            "Should have 2 groups"
        );
        const groupHeaders = Array.from(
            target.querySelectorAll(".o-map-renderer--pin-list-group-header")
        );
        assert.deepEqual(
            groupHeaders.map((gh) => gh.innerText),
            ["Bar", "Foo"]
        );
        assert.containsN(target, ".o-map-renderer--pin-list-details", 2);
        assert.containsN(target, ".o-map-renderer--pin-list-details li", 3);
        let details = Array.from(target.querySelectorAll(".o-map-renderer--pin-list-details"));
        assert.deepEqual(
            details.map((d) => d.innerText),
            ["FooProject\nFooBarProject", "BarProject"]
        );

        await click(target.querySelectorAll(".o-map-renderer--pin-list-group-header")[1]);

        assert.containsN(
            target,
            ".o-map-renderer--pin-list-group-header",
            2,
            "Should still have 2 groups"
        );
        assert.containsOnce(target, ".o-map-renderer--pin-list-details");
        assert.containsN(target, ".o-map-renderer--pin-list-details li", 2);
        details = Array.from(target.querySelectorAll(".o-map-renderer--pin-list-details"));
        assert.deepEqual(
            details.map((d) => d.innerText),
            ["FooProject\nFooBarProject"]
        );

        await click(target.querySelectorAll(".o-map-renderer--pin-list-group-header")[0]);

        assert.containsNone(target, ".o-map-renderer--pin-list-details");

        await click(target.querySelectorAll(".o-map-renderer--pin-list-group-header")[1]);

        assert.containsOnce(target, ".o-map-renderer--pin-list-details");
        assert.containsOnce(target, ".o-map-renderer--pin-list-details li");
        details = Array.from(target.querySelectorAll(".o-map-renderer--pin-list-details"));
        assert.deepEqual(
            details.map((d) => d.innerText),
            ["BarProject"]
        );
    });

    QUnit.test("Toggle grouped one2many pin lists", async function (assert) {
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const records = serverData.models["project.task"].threeRecords;
        const partners = serverData.models["res.partner"].records;

        for (const record of records.records) {
            // add name on partner_id to have name_get like value
            record.partner_id.push(partners.find((x) => x.id === record.partner_id[0]).name);
        }
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id"/>`,
            groupBy: ["partner_ids"],
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return records;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return partners;
                }
            },
        });

        assert.containsN(
            target,
            ".o-map-renderer--pin-list-group-header",
            3,
            "Should have 3 groups"
        );

        const groupHeaders = Array.from(
            target.querySelectorAll(".o-map-renderer--pin-list-group-header")
        );
        assert.deepEqual(
            groupHeaders.map((gh) => gh.innerText),
            ["Foo", "Foo", "Bar"]
        );

        assert.containsN(target, ".o-map-renderer--pin-list-details", 3);
        assert.containsN(target, ".o-map-renderer--pin-list-details li", 5);
        const details = Array.from(target.querySelectorAll(".o-map-renderer--pin-list-details"));
        assert.deepEqual(
            details.map((d) => d.innerText),
            ["FooProject\nBarProject\nFooBarProject", "FooProject", "BarProject"]
        );
        assert.containsN(target, ".leaflet-marker-icon", 3);
    });

    QUnit.test("Check groupBy on datetime field", async function (assert) {
        assert.expect(1);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        serverData.models["project.task"].fields["scheduled_date"] = {
            string: "Schedule date",
            type: "datetime",
        };
        serverData.models["project.task"].records = [
            { id: 1, name: "FooProject", sequence: 1, partner_id: 1, scheduled_date: false },
        ];

        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].twoRecordsAddressCoordinates;
                }
            },
            searchViewId: false,
            searchViewArch: `
                <search>
                    <group expand='0' string='Group By'>
                        <filter string="scheduled_date" name="scheduled_date" context="{'group_by': 'scheduled_date'}"/>
                    </group>
                </search>
            `,
        });

        assert.containsNone(
            target,
            ".o-map-renderer--pin-list-group-header",
            "Should not have any groups"
        );

        await toggleGroupByMenu(target);

        // don't throw an error when grouping a field with a false value
        await toggleMenuItem(target, "scheduled_date");
        await toggleMenuItemOption(target, "scheduled_date", "year");
    });

    QUnit.test("Change groupBy", async function (assert) {
        assert.expect(13);
        patchWithCleanup(session, { map_box_token: MAP_BOX_TOKEN });
        const records = serverData.models["project.task"].threeRecords;
        const partners = serverData.models["res.partner"].twoRecordsAddressCoordinates;
        for (const record of records.records) {
            // add name on partner_id to have name_get like value
            record.partner_id.push(partners.find((x) => x.id === record.partner_id[0]).name);
        }
        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return records;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return partners;
                }
            },
            searchViewId: false,
            searchViewArch: `
                <search>
                    <filter string="Partner" name="partner_id" context="{'group_by': 'partner_id'}"/>
                    <filter string="Name" name="display_name" context="{'group_by': 'display_name'}"/>
                </search>
            `,
        });

        assert.containsNone(
            target,
            ".o-map-renderer--pin-list-group-header",
            "Should not have any groups"
        );

        await toggleGroupByMenu(target);
        await toggleMenuItem(target, "Partner");

        assert.containsN(
            target,
            ".o-map-renderer--pin-list-group-header",
            2,
            "Should have 2 groups"
        );
        let groupHeaders = Array.from(
            target.querySelectorAll(".o-map-renderer--pin-list-group-header")
        );
        assert.deepEqual(
            groupHeaders.map((gh) => gh.innerText),
            ["Bar", "Foo"]
        );
        // Groups should be loaded too
        assert.containsN(target, ".o-map-renderer--pin-list-details li", 3);
        let details = Array.from(target.querySelectorAll(".o-map-renderer--pin-list-details"));
        assert.deepEqual(
            details.map((d) => d.innerText),
            ["FooProject\nFooBarProject", "BarProject"]
        );

        await toggleMenuItem(target, "Name");

        groupHeaders = Array.from(
            target.querySelectorAll(".o-map-renderer--pin-list-group-header")
        );
        assert.deepEqual(
            groupHeaders.map((gh) => gh.innerText),
            ["Bar", "Foo"],
            "Should not have changed"
        );
        details = Array.from(target.querySelectorAll(".o-map-renderer--pin-list-details"));
        assert.deepEqual(
            details.map((d) => d.innerText),
            ["FooProject\nFooBarProject", "BarProject"]
        );

        await toggleMenuItem(target, "Partner");

        assert.containsN(
            target,
            ".o-map-renderer--pin-list-group-header",
            3,
            "Should have 3 groups"
        );
        groupHeaders = Array.from(
            target.querySelectorAll(".o-map-renderer--pin-list-group-header")
        );
        assert.deepEqual(
            groupHeaders.map((gh) => gh.innerText),
            ["FooProject", "BarProject", "FooBarProject"]
        );
        details = Array.from(target.querySelectorAll(".o-map-renderer--pin-list-details"));
        assert.deepEqual(
            details.map((d) => d.innerText),
            ["FooProject", "BarProject", "FooBarProject"]
        );
        assert.containsOnce(target.querySelectorAll(".o-map-renderer--pin-list-details")[0], "li");
        assert.containsOnce(target.querySelectorAll(".o-map-renderer--pin-list-details")[1], "li");
        assert.containsOnce(target.querySelectorAll(".o-map-renderer--pin-list-details")[2], "li");
    });

    //--------------------------------------------------------------------------
    // Controller testing
    //--------------------------------------------------------------------------

    QUnit.test("Click on open button switches to form view", async function (assert) {
        assert.expect(7);

        serviceRegistry.add(
            "action",
            {
                start() {
                    return {
                        switchView(name, info) {
                            assert.step("switchView");
                            assert.strictEqual(name, "form", "The view switched to should be form");
                            assert.strictEqual(info.resId, 1, "The record's id should be 1");
                            assert.strictEqual(
                                info.mode,
                                "readonly",
                                "The mode should be readonly"
                            );
                            assert.strictEqual(
                                info.model,
                                "project.task",
                                "The form view should be on the 'project.task' model"
                            );
                        },
                    };
                },
            },
            { force: true }
        );
        serverData.views = {
            "project.task,false,form": "<form/>",
        };

        await makeView({
            config: { views: [[false, "form"]] },
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1" />`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].oneLocatedRecord;
                }
            },
        });

        await click(target, "div.leaflet-marker-icon");
        assert.containsOnce(
            target,
            "div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open",
            "The button should be present in the dom"
        );
        await click(
            target,
            "div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open"
        );
        assert.verifySteps(["switchView"]);
    });

    QUnit.test("Test the lack of open button", async function (assert) {
        assert.expect(1);

        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id"></map>`,
            async mockRPC(route) {
                switch (route) {
                    case "/web/dataset/call_kw/project.task/web_search_read":
                        return serverData.models["project.task"].oneRecord;
                    case "/web/dataset/call_kw/res.partner/search_read":
                        return serverData.models["res.partner"].oneLocatedRecord;
                }
            },
        });

        await click(target, "div.leaflet-marker-icon");

        assert.containsNone(
            target,
            "div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open",
            "The button should not be present in the dom"
        );
    });

    QUnit.test(
        "attribute panel_title on the arch should display in the pin list",
        async function (assert) {
            assert.expect(1);

            await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="partner_id" panel_title="AAAAAAAAAAAAAAAAA"></map>`,
                async mockRPC(route) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].oneRecord;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].oneLocatedRecord;
                    }
                },
            });

            assert.strictEqual(
                target.querySelector(".o-map-renderer--pin-list-container .o_pin_list_header span")
                    .textContent,
                "AAAAAAAAAAAAAAAAA"
            );
        }
    );

    QUnit.test(
        "Test using a field other than partner_id for the map view",
        async function (assert) {
            assert.expect(1);

            await makeView({
                serverData,
                type: "map",
                resModel: "project.task",
                arch: `<map res_partner="another_partner_id"></map>`,
                async mockRPC(route) {
                    switch (route) {
                        case "/web/dataset/call_kw/project.task/web_search_read":
                            return serverData.models["project.task"].anotherPartnerId;
                        case "/web/dataset/call_kw/res.partner/search_read":
                            return serverData.models["res.partner"].oneLocatedRecord;
                    }
                },
            });

            await click(target, "div.leaflet-marker-icon");

            assert.containsNone(
                target,
                "div.leaflet-popup-pane button.btn.btn-primary.o-map-renderer--popup-buttons-open",
                "The button should not be present in the dom"
            );
        }
    );

    QUnit.test("Check Google Maps URL is updating on domain change", async function (assert) {
        assert.expect(2);

        serverData.models["project.task"].records = [
            { id: 1, name: "FooProject", sequence: 1, partner_id: 2 },
            { id: 2, name: "BarProject", sequence: 2, partner_id: 3 },
        ];

        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id"/>`,
            searchViewArch: `
                        <search>
                            <filter name="some_filter" string="FooProject only" domain="[['name', '=', 'FooProject']]"/>
                        </search>`,
        });

        assert.strictEqual(
            target.querySelector("a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&waypoints=10,10.5|11,11.5",
            "The link's URL initially should contain the coordinates for all records"
        );

        //apply domain and check that the Google Maps URL on the button reflects the changes
        await toggleFilterMenu(target);
        await toggleMenuItem(target, "FooProject only");
        assert.strictEqual(
            target.querySelector("a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&waypoints=10,10.5",
            "The link's URL after domain is applied should only contain coordinates for filtered records"
        );
    });

    QUnit.test("Check Google Maps URL (routing and multiple records)", async function (assert) {
        assert.expect(1);

        serverData.models["project.task"].records = [
            { id: 1, name: "FooProject", sequence: 1, partner_id: 2 },
            { id: 2, name: "BarProject", sequence: 2, partner_id: 3 },
        ];

        await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1"/>`,
        });

        assert.strictEqual(
            target.querySelector("a.btn.btn-primary").href,
            "https://www.google.com/maps/dir/?api=1&destination=11,11.5&waypoints=10,10.5",
            "The link's URL initially should contain the coordinates for all records"
        );
    });

    QUnit.test("Do not notify if unmounted after fetching coordinate", async function (assert) {
        const def = makeDeferred();

        patchWithCleanup(MapModel.prototype, {
            _fetchCoordinatesFromAddressOSM() {
                return def;
            },
            _notifyFetchedCoordinate() {
                assert.step("notify");
            },
        });

        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1"/>`,
        });

        destroy(map);

        def.resolve();
        await nextTick();

        assert.verifySteps([]);
    });

    QUnit.test("Do not fetch if unmounted after waiting interval", async function (assert) {
        const def = makeDeferred();

        patchWithCleanup(browser, {
            setTimeout(fn) {
                def.then(fn);
            },
        });

        patchWithCleanup(MapModel.prototype, {
            async _fetchCoordinatesFromAddressOSM() {
                assert.step("fetch");
                return [];
            },
            _notifyFetchedCoordinate() {
                assert.step("notify");
            },
        });

        const map = await makeView({
            serverData,
            type: "map",
            resModel: "project.task",
            arch: `<map res_partner="partner_id" routing="1"/>`,
        });

        destroy(map);

        def.resolve();
        await nextTick();

        assert.verifySteps([]);
    });
});
