Skip to content

Commit f11ba91

Browse files
committed
[ADD] project_task_code_portal: add portal report tests
Add test coverage for portal task report generation and project sharing functionality: - Add test for HTML report generation through portal - Add test for project sharing view with task code display - Cover missing lines in portal controller Task: 3241
1 parent 401720b commit f11ba91

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed

project_task_code_portal/tests/test_portal.py

+113
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Copyright 2025 Cetmix OÜ
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
33

4+
import re
5+
46
from lxml import html
57

68
from odoo import Command, tools
@@ -84,6 +86,117 @@ def test_portal_task_search_link_format(self):
8486
self.url_task_code_pattern.format(self.task_1.code)[:-1] + query_params,
8587
)
8688

89+
def test_portal_task_report(self):
90+
"""Test task report generation through portal."""
91+
self.authenticate("portal", "portal")
92+
# Check if hr_timesheet module is installed
93+
hr_timesheet_installed = bool(
94+
self.env["ir.module.module"].search(
95+
[("name", "=", "hr_timesheet"), ("state", "=", "installed")]
96+
)
97+
)
98+
response = self.url_open(self.base_url + self.task_1.code + "?report_type=html")
99+
if hr_timesheet_installed:
100+
# If hr_timesheet is installed, expect successful response
101+
# _show_task_report is overridden by hr_timesheet to generate timesheet reports
102+
self.assertEqual(response.status_code, 200)
103+
self.assertIn("text/html", response.headers.get("Content-Type", ""))
104+
else:
105+
# If hr_timesheet is not installed, expect error response
106+
# _show_task_report raises MissingError("There is nothing to report.")
107+
# This method is to be overriden to report timesheets if the module is installed
108+
self.assertEqual(response.status_code, 400)
109+
content = response.content
110+
tree = html.fromstring(content)
111+
error_elements = tree.xpath(
112+
"//pre[contains(text(), 'There is nothing to report.')]"
113+
)
114+
self.assertTrue(
115+
error_elements,
116+
"Error message 'There is nothing to report.' not found in response",
117+
)
118+
119+
def test_portal_task_project_sharing(self):
120+
"""Test project sharing functionality."""
121+
self.authenticate("portal", "portal")
122+
123+
# First, access multiple tasks to build up history
124+
other_task = self.env["project.task"].create(
125+
{
126+
"name": "Other Task",
127+
"project_id": self.task_1.project_id.id,
128+
}
129+
)
130+
self.url_open(f"{self.base_url}{other_task.code}")
131+
132+
# Share the project with portal user
133+
project_share_wizard = self.env["project.share.wizard"].create(
134+
{
135+
"access_mode": "edit",
136+
"res_model": "project.project",
137+
"res_id": self.task_1.project_id.id,
138+
"partner_ids": [Command.link(self.partner_portal.id)],
139+
}
140+
)
141+
project_share_wizard.action_send_mail()
142+
143+
# Get the sharing link from the most recent mail message
144+
message = self.env["mail.message"].search(
145+
[
146+
("partner_ids", "in", self.partner_portal.id),
147+
("model", "=", "project.project"),
148+
("res_id", "=", self.task_1.project_id.id),
149+
],
150+
order="id DESC",
151+
limit=1,
152+
)
153+
154+
share_link = str(message.body.split('href="')[1].split('">')[0])
155+
match = re.search(
156+
r"access_token=([^&]+)&pid=([^&]+)&hash=([^&]*)", share_link
157+
)
158+
access_token, pid, _hash = match.groups()
159+
160+
# Access the task with project sharing context
161+
url = f"{self.base_url}{self.task_1.code}"
162+
163+
# Get initial response to extract CSRF token
164+
initial_response = self.url_open(url)
165+
content = initial_response.text
166+
csrf_token = re.search(r'csrf_token: "([^"]+)"', content).group(1)
167+
168+
# Make the POST request with CSRF token and project_sharing=True
169+
response = self.url_open(
170+
url=url,
171+
data={
172+
"csrf_token": csrf_token,
173+
"access_token": access_token,
174+
"project_sharing": True,
175+
"pid": pid,
176+
"hash": _hash,
177+
},
178+
)
179+
180+
self.assertEqual(response.status_code, 200)
181+
182+
# Now check if the navigation links for previous/next task are not present
183+
# This would indicate that the history was reset to only contain the current task
184+
content = response.content
185+
tree = html.fromstring(content)
186+
187+
# Check for absence of navigation links (prev/next)
188+
# which would be present if history had multiple tasks
189+
prev_links = tree.xpath("//a[contains(@class, 'o_portal_pager_previous')]")
190+
next_links = tree.xpath("//a[contains(@class, 'o_portal_pager_next')]")
191+
192+
# If history was reset to only current task, there should be no prev/next links
193+
self.assertFalse(
194+
prev_links, "Previous task link should not be present if history was reset"
195+
)
196+
self.assertFalse(
197+
next_links, "Next task link should not be present if history was reset"
198+
)
199+
87200

88201
@tagged("-at_install", "post_install")
89202
class TestPortalProjectTaskCode(TestProjectPortalCommon, HttpCaseWithUserPortal):

0 commit comments

Comments
 (0)