feat(web): расписание, каналы уведомлений, история проверок, drift-badge
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
import { render, screen, waitFor } from "@testing-library/react"
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import { MemoryRouter } from "react-router-dom"
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
||||
import { ChannelsPage } from "./ChannelsPage"
|
||||
import { AuthProvider } from "@/auth/AuthContext"
|
||||
import { api } from "@/api/client"
|
||||
import { vi, beforeEach, test, expect } from "vitest"
|
||||
import type { Channel } from "@/api/types"
|
||||
|
||||
const PROJECT_ID = "p1"
|
||||
const channels: Channel[] = [
|
||||
{ id: "c1", type: "telegram", config: { chat_id: "123456" }, enabled: true },
|
||||
{ id: "c2", type: "webhook", config: { url: "https://hooks.example.com/x" }, enabled: false },
|
||||
]
|
||||
|
||||
function renderPage() {
|
||||
const qc = new QueryClient()
|
||||
return render(
|
||||
<QueryClientProvider client={qc}>
|
||||
<AuthProvider>
|
||||
<MemoryRouter initialEntries={["/channels"]}>
|
||||
<ChannelsPage />
|
||||
</MemoryRouter>
|
||||
</AuthProvider>
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
vi.spyOn(api.auth, "me").mockResolvedValue({
|
||||
user: { id: "u1", email: "a@b.com" },
|
||||
project: { id: PROJECT_ID, name: "Default" },
|
||||
})
|
||||
vi.spyOn(api, "listChannels").mockResolvedValue(channels)
|
||||
})
|
||||
|
||||
test("отрисовывает список каналов без секрета", async () => {
|
||||
renderPage()
|
||||
|
||||
expect(await screen.findByText("telegram")).toBeInTheDocument()
|
||||
expect(screen.getByText("webhook")).toBeInTheDocument()
|
||||
expect(screen.getByText(/123456/)).toBeInTheDocument()
|
||||
expect(screen.getByText(/hooks\.example\.com/)).toBeInTheDocument()
|
||||
|
||||
expect(document.body.textContent).not.toMatch(/bot_token/i)
|
||||
expect(screen.queryByDisplayValue(/123456/)).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("создание telegram-канала собирает config.chat_id + secret=bot_token", async () => {
|
||||
const createSpy = vi.spyOn(api, "createChannel").mockResolvedValue({
|
||||
id: "c3", type: "telegram", config: { chat_id: "999" }, enabled: true,
|
||||
})
|
||||
const user = userEvent.setup()
|
||||
renderPage()
|
||||
|
||||
await screen.findByText("telegram")
|
||||
|
||||
await user.click(screen.getByRole("combobox", { name: /тип канала/i }))
|
||||
await user.click(await screen.findByRole("option", { name: /telegram/i }))
|
||||
|
||||
await user.type(screen.getByLabelText(/chat id/i), "999")
|
||||
await user.type(screen.getByLabelText(/bot token/i), "SECRET_TOKEN")
|
||||
await user.click(screen.getByRole("button", { name: /добавить канал/i }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(createSpy).toHaveBeenCalledWith(PROJECT_ID, {
|
||||
type: "telegram",
|
||||
config: { chat_id: "999" },
|
||||
secret: "SECRET_TOKEN",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(document.body.textContent).not.toMatch(/SECRET_TOKEN/)
|
||||
})
|
||||
|
||||
test("создание webhook-канала собирает config.url без секрета", async () => {
|
||||
const createSpy = vi.spyOn(api, "createChannel").mockResolvedValue({
|
||||
id: "c4", type: "webhook", config: { url: "https://hooks.example.com/y" }, enabled: true,
|
||||
})
|
||||
const user = userEvent.setup()
|
||||
renderPage()
|
||||
|
||||
await screen.findByText("telegram")
|
||||
|
||||
await user.click(screen.getByRole("combobox", { name: /тип канала/i }))
|
||||
await user.click(await screen.findByRole("option", { name: /webhook/i }))
|
||||
|
||||
await user.type(screen.getByLabelText(/url/i), "https://hooks.example.com/y")
|
||||
await user.click(screen.getByRole("button", { name: /добавить канал/i }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(createSpy).toHaveBeenCalledWith(PROJECT_ID, {
|
||||
type: "webhook",
|
||||
config: { url: "https://hooks.example.com/y" },
|
||||
secret: "",
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
test("удаление канала вызывает api.deleteChannel", async () => {
|
||||
const deleteSpy = vi.spyOn(api, "deleteChannel").mockResolvedValue(undefined)
|
||||
vi.spyOn(window, "confirm").mockReturnValue(true)
|
||||
const user = userEvent.setup()
|
||||
renderPage()
|
||||
|
||||
await screen.findByText("telegram")
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /удалить канал telegram/i }))
|
||||
|
||||
await waitFor(() => expect(deleteSpy).toHaveBeenCalledWith(PROJECT_ID, "c1"))
|
||||
})
|
||||
|
||||
test("кнопка «Тест» вызывает api.testChannel", async () => {
|
||||
const testSpy = vi.spyOn(api, "testChannel").mockResolvedValue({ status: "ok" })
|
||||
const user = userEvent.setup()
|
||||
renderPage()
|
||||
|
||||
await screen.findByText("telegram")
|
||||
|
||||
const testButtons = screen.getAllByRole("button", { name: /тест/i })
|
||||
await user.click(testButtons[0])
|
||||
|
||||
await waitFor(() => expect(testSpy).toHaveBeenCalledWith(PROJECT_ID, "c1"))
|
||||
})
|
||||
|
||||
test("ошибка тест-отправки отображается как alert", async () => {
|
||||
vi.spyOn(api, "testChannel").mockRejectedValue(new Error("Канал не отвечает"))
|
||||
const user = userEvent.setup()
|
||||
renderPage()
|
||||
|
||||
await screen.findByText("telegram")
|
||||
|
||||
const testButtons = screen.getAllByRole("button", { name: /тест/i })
|
||||
await user.click(testButtons[0])
|
||||
|
||||
expect(await screen.findByRole("alert")).toHaveTextContent("Канал не отвечает")
|
||||
})
|
||||
|
||||
test("пустое состояние при отсутствии каналов", async () => {
|
||||
vi.spyOn(api, "listChannels").mockResolvedValue([])
|
||||
renderPage()
|
||||
|
||||
expect(await screen.findByText(/каналов пока нет/i)).toBeInTheDocument()
|
||||
})
|
||||
Reference in New Issue
Block a user