feat: enhance duty information handling with contact details and current duty view
- Added `bot_username` to settings for dynamic retrieval of the bot's username. - Implemented `_resolve_bot_username` function to fetch the bot's username if not set, improving user experience in group chats. - Updated `DutyWithUser` schema to include optional `phone` and `username` fields for enhanced duty information. - Enhanced API responses to include contact details for users, ensuring better communication. - Introduced a new current duty view in the web app, displaying active duty information along with contact options. - Updated CSS styles for better presentation of contact information in duty cards. - Added unit tests to verify the inclusion of contact details in API responses and the functionality of the current duty view.
This commit is contained in:
121
webapp/js/currentDuty.test.js
Normal file
121
webapp/js/currentDuty.test.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Unit tests for currentDuty (findCurrentDuty, renderCurrentDutyContent, showCurrentDutyView).
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll, vi } from "vitest";
|
||||
|
||||
vi.mock("./api.js", () => ({
|
||||
fetchDuties: vi.fn().mockResolvedValue([])
|
||||
}));
|
||||
|
||||
import {
|
||||
findCurrentDuty,
|
||||
renderCurrentDutyContent
|
||||
} from "./currentDuty.js";
|
||||
|
||||
describe("currentDuty", () => {
|
||||
beforeAll(() => {
|
||||
document.body.innerHTML =
|
||||
'<div id="loading"></div>' +
|
||||
'<div class="container">' +
|
||||
'<div id="calendarSticky"></div>' +
|
||||
'<div id="dutyList"></div>' +
|
||||
'<div id="currentDutyView" class="current-duty-view hidden"></div>' +
|
||||
"</div>";
|
||||
});
|
||||
|
||||
describe("findCurrentDuty", () => {
|
||||
it("returns duty when now is between start_at and end_at", () => {
|
||||
const now = new Date();
|
||||
const start = new Date(now);
|
||||
start.setHours(start.getHours() - 1, 0, 0, 0);
|
||||
const end = new Date(now);
|
||||
end.setHours(end.getHours() + 1, 0, 0, 0);
|
||||
const duties = [
|
||||
{
|
||||
event_type: "duty",
|
||||
full_name: "Иванов",
|
||||
start_at: start.toISOString(),
|
||||
end_at: end.toISOString()
|
||||
}
|
||||
];
|
||||
const duty = findCurrentDuty(duties);
|
||||
expect(duty).not.toBeNull();
|
||||
expect(duty.full_name).toBe("Иванов");
|
||||
});
|
||||
|
||||
it("returns null when no duty overlaps current time", () => {
|
||||
const duties = [
|
||||
{
|
||||
event_type: "duty",
|
||||
full_name: "Past",
|
||||
start_at: "2020-01-01T09:00:00Z",
|
||||
end_at: "2020-01-01T17:00:00Z"
|
||||
},
|
||||
{
|
||||
event_type: "duty",
|
||||
full_name: "Future",
|
||||
start_at: "2030-01-01T09:00:00Z",
|
||||
end_at: "2030-01-01T17:00:00Z"
|
||||
}
|
||||
];
|
||||
expect(findCurrentDuty(duties)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("renderCurrentDutyContent", () => {
|
||||
it("renders no-duty message and back button when duty is null", () => {
|
||||
const html = renderCurrentDutyContent(null, "en");
|
||||
expect(html).toContain("current-duty-card");
|
||||
expect(html).toContain("Current Duty");
|
||||
expect(html).toContain("No one is on duty right now");
|
||||
expect(html).toContain("Back to calendar");
|
||||
expect(html).toContain('data-action="back"');
|
||||
});
|
||||
|
||||
it("renders duty card with name, shift, and back button when duty has no contacts", () => {
|
||||
const duty = {
|
||||
event_type: "duty",
|
||||
full_name: "Иванов Иван",
|
||||
start_at: "2025-03-02T06:00:00.000Z",
|
||||
end_at: "2025-03-03T06:00:00.000Z"
|
||||
};
|
||||
const html = renderCurrentDutyContent(duty, "ru");
|
||||
expect(html).toContain("Текущее дежурство");
|
||||
expect(html).toContain("Иванов Иван");
|
||||
expect(html).toContain("Смена");
|
||||
expect(html).toContain("Назад к календарю");
|
||||
expect(html).toContain('data-action="back"');
|
||||
});
|
||||
|
||||
it("renders duty card with phone and Telegram links when present", () => {
|
||||
const duty = {
|
||||
event_type: "duty",
|
||||
full_name: "Alice",
|
||||
start_at: "2025-03-02T09:00:00",
|
||||
end_at: "2025-03-02T17:00:00",
|
||||
phone: "+7 900 123-45-67",
|
||||
username: "alice_dev"
|
||||
};
|
||||
const html = renderCurrentDutyContent(duty, "en");
|
||||
expect(html).toContain("Alice");
|
||||
expect(html).toContain("current-duty-contact-row");
|
||||
expect(html).toContain('href="tel:');
|
||||
expect(html).toContain("+7 900 123-45-67");
|
||||
expect(html).toContain("https://t.me/");
|
||||
expect(html).toContain("alice_dev");
|
||||
expect(html).toContain("Back to calendar");
|
||||
});
|
||||
});
|
||||
|
||||
describe("showCurrentDutyView", () => {
|
||||
it("hides the global loading element when called", async () => {
|
||||
vi.resetModules();
|
||||
const { showCurrentDutyView } = await import("./currentDuty.js");
|
||||
await showCurrentDutyView(() => {});
|
||||
const loading = document.getElementById("loading");
|
||||
expect(loading).not.toBeNull();
|
||||
expect(loading.classList.contains("hidden")).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user