diff --git a/addons/project/static/tests/tours/project_sharing_tour.js b/addons/project/static/tests/tours/project_sharing_tour.js
new file mode 100644
index 00000000000..ea9fb09c261
--- /dev/null
+++ b/addons/project/static/tests/tours/project_sharing_tour.js
@@ -0,0 +1,116 @@
+/** @odoo-module **/
+
+import tour from 'web_tour.tour';
+
+tour.register('project_sharing_tour', {
+ test: true,
+ url: '/web',
+}, [...tour.stepUtils.goToAppSteps("project.menu_main_pm", 'Go to the Project App.'), {
+ // an invisible element cannot be used as a trigger so this small hack is mandatory for the next step
+ trigger: '.o_kanban_record:contains("Project Sharing")',
+ run: function () {
+ this.$anchor.find('.o_dropdown_kanban').css('visibility', 'visible');
+ },
+}, {
+ trigger: '.oe_kanban_global_click :contains("Project Sharing") .o_dropdown_kanban',
+ content: 'Open the project dropdown.'
+}, {
+ trigger: '.o_kanban_record:contains("Project Sharing") .dropdown-menu a:contains("Share")',
+ content: 'Start editing the project.',
+}, {
+ trigger: 'div.o_field_radio[name="access_mode"] > div.o_radio_item > input[data-value="edit"]',
+ content: 'Select "Edit" as Access mode in the "Share Project" wizard.',
+}, {
+ trigger: '.o_field_many2one[name="partner_ids"]',
+ content: 'Select the user portal as collaborator to the "Project Sharing" project.',
+ run: function (actions) {
+ actions.text('Georges', this.$anchor.find('input'));
+ },
+}, {
+ trigger: '.ui-autocomplete a:contains("Georges")',
+ in_modal: false,
+}, {
+ trigger: 'footer > button[name="action_send_mail"]',
+ content: 'Confirm the project sharing with this portal user.',
+}, {
+ trigger: '.o_web_client',
+ content: 'Go to project portal view to select the "Project Sharing" project',
+ run: function () {
+ window.location.href = window.location.origin + '/my/projects';
+ },
+}, {
+ trigger: 'table > tbody > tr a:has(span:contains(Project Sharing))',
+ content: 'Select "Project Sharing" project to go to project sharing feature for this project.',
+}, {
+ trigger: '.o_project_sharing',
+ content: 'Wait the project sharing feature be loaded',
+ run: function () {},
+}, {
+ trigger: 'button.o-kanban-button-new',
+ content: 'Click "Create" button',
+ run: 'click',
+}, {
+ trigger: '.o_kanban_quick_create .o_field_widget[name="name"]',
+ content: 'Create Task',
+ run: 'text Test Create Task',
+}, {
+ trigger: '.o_kanban_quick_create .o_kanban_edit',
+ content: 'Go to the form view of this new task',
+}, {
+ trigger: 'div.o_statusbar_status[name="stage_id"] button[aria-checked="false"]:contains(Done)',
+ content: 'Change the stage of the task.',
+}, {
+ trigger: '.o_portal_chatter_composer_input .o_portal_chatter_composer_body textarea[name="message"]',
+ content: 'Write a message in the chatter of the task',
+ run: 'text I create a new task for testing purpose.',
+}, {
+ trigger: '.o_portal_chatter_composer_input .o_portal_chatter_composer_body button[data-action="/mail/chatter_post"]',
+ content: 'Send the message',
+}, {
+ trigger: 'ol.breadcrumb > li.o_back_button > a:contains(Project Sharing)',
+ content: 'Go back to the kanban view',
+}, {
+ trigger: 'button.o_switch_view.o_list',
+ content: 'Go to the list view',
+ run: 'click',
+}]);
+
+tour.register('portal_project_sharing_tour', {
+ test: true,
+ url: '/my/projects',
+}, [{
+ trigger: 'table > tbody > tr a:has(span:contains(Project Sharing))',
+ content: 'Select "Project Sharing" project to go to project sharing feature for this project.',
+}, {
+ trigger: '.o_project_sharing',
+ content: 'Wait the project sharing feature be loaded',
+ run: function () {},
+}, {
+ trigger: 'button.o-kanban-button-new',
+ content: 'Click "Create" button',
+ run: 'click',
+}, {
+ trigger: '.o_kanban_quick_create .o_field_widget[name="name"]',
+ content: 'Create Task',
+ run: 'text Test Create Task',
+}, {
+ trigger: '.o_kanban_quick_create .o_kanban_edit',
+ content: 'Go to the form view of this new task',
+}, {
+ trigger: 'div.o_statusbar_status[name="stage_id"] button[aria-checked="false"]:contains(Done)',
+ content: 'Change the stage of the task.',
+}, {
+ trigger: '.o_portal_chatter_composer_input .o_portal_chatter_composer_body textarea[name="message"]',
+ content: 'Write a message in the chatter of the task',
+ run: 'text I create a new task for testing purpose.',
+}, {
+ trigger: '.o_portal_chatter_composer_input .o_portal_chatter_composer_body button[data-action="/mail/chatter_post"]',
+ content: 'Send the message',
+}, {
+ trigger: 'ol.breadcrumb > li.o_back_button > a:contains(Project Sharing)',
+ content: 'Go back to the kanban view',
+}, {
+ trigger: 'button.o_switch_view.o_list',
+ content: 'Go to the list view',
+ run: 'click',
+}]);
diff --git a/addons/project/tests/__init__.py b/addons/project/tests/__init__.py
index b79de62503e..6f85611241d 100644
--- a/addons/project/tests/__init__.py
+++ b/addons/project/tests/__init__.py
@@ -7,6 +7,7 @@ from . import test_project_config
from . import test_project_flow
from . import test_project_recurrence
from . import test_project_sharing
+from . import test_project_sharing_ui
from . import test_project_subtasks
from . import test_project_ui
from . import test_project_update_access_rights
diff --git a/addons/project/tests/test_project_sharing_ui.py b/addons/project/tests/test_project_sharing_ui.py
new file mode 100644
index 00000000000..70de1324f99
--- /dev/null
+++ b/addons/project/tests/test_project_sharing_ui.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+from odoo import Command
+from odoo.tests import HttpCase, tagged
+
+
+@tagged('post_install', '-at_install')
+class TestProjectSharingUi(HttpCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ user = cls.env['res.users'].with_context({'no_reset_password': True, 'mail_create_nolog': True}).create({
+ 'name': 'Georges',
+ 'login': 'georges1',
+ 'password': 'georges1',
+ 'email': 'georges@project.portal',
+ 'signature': 'SignGeorges',
+ 'notification_type': 'email',
+ 'groups_id': [Command.set([cls.env.ref('base.group_portal').id])],
+ })
+
+ cls.partner_portal = cls.env['res.partner'].with_context({'mail_create_nolog': True}).create({
+ 'name': 'Georges',
+ 'email': 'georges@project.portal',
+ 'company_id': False,
+ 'user_ids': [user.id],
+ })
+ cls.project_portal = cls.env['project.project'].with_context({'mail_create_nolog': True}).create({
+ 'name': 'Project Sharing',
+ 'privacy_visibility': 'portal',
+ 'alias_name': 'project+sharing',
+ 'partner_id': cls.partner_portal.id,
+ 'type_ids': [
+ Command.create({'name': 'To Do', 'sequence': 1}),
+ Command.create({'name': 'Done', 'sequence': 10})
+ ],
+ })
+
+ def test_01_project_sharing(self):
+ """ Test Project Sharing UI with an internal user """
+ self.start_tour("/web", 'project_sharing_tour', login="admin")
+
+ def test_02_project_sharing(self):
+ """ Test project sharing ui with a portal user.
+
+ The additional data created here are the data created in the first test with the tour js.
+
+ Since a problem to logout Mitchell Admin to log in as Georges user, this test is created
+ to launch a tour with portal user.
+ """
+ project_share_wizard = self.env['project.share.wizard'].create({
+ 'access_mode': 'edit',
+ 'res_model': 'project.project',
+ 'res_id': self.project_portal.id,
+ 'partner_ids': [
+ Command.link(self.partner_portal.id),
+ ],
+ })
+ project_share_wizard.action_send_mail()
+
+ self.project_portal.write({
+ 'task_ids': [Command.create({
+ 'name': "Test Project Sharing",
+ 'stage_id': self.project_portal.type_ids.filtered(lambda stage: stage.sequence == 10)[:1].id,
+ })],
+ })
+ self.start_tour("/my/projects", 'portal_project_sharing_tour', login='georges1')
diff --git a/addons/project/views/project_sharing_templates.xml b/addons/project/views/project_sharing_templates.xml
index 0dcc2e574a5..38dda9405d8 100644
--- a/addons/project/views/project_sharing_templates.xml
+++ b/addons/project/views/project_sharing_templates.xml
@@ -24,6 +24,7 @@
+