ICTU/quality-time

View on GitHub
components/frontend/src/issue/IssueStatus.test.js

Summary

Maintainability
F
1 wk
Test Coverage
import { render, screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import history from "history/browser"

import { createTestableSettings } from "../__fixtures__/fixtures"
import { ISSUE_STATUS_COLORS } from "../utils"
import { IssueStatus } from "./IssueStatus"

function renderIssueStatus({
    connectionError = false,
    created = true,
    due = false,
    issueTrackerMissing = false,
    landingUrl = "https://issue",
    parseError = false,
    status = "in progress",
    statusCategory = "",
    release = false,
    releaseName = "1.0",
    releaseReleased = false,
    releaseDate = "3000-01-02",
    sprint = false,
    sprintEndDate = "3000-01-01",
    updated = false,
} = {}) {
    const settings = createTestableSettings()
    let creationDate = new Date()
    creationDate.setDate(creationDate.getDate() - 4)
    let updateDate = new Date()
    updateDate.setDate(updateDate.getDate() - 2)
    let dueDate = new Date()
    dueDate.setDate(dueDate.getDate() + 2)
    const issueStatus = {
        issue_id: "123",
        name: status,
        summary: "Issue summary",
        created: created ? creationDate.toISOString() : null,
        updated: updated ? updateDate.toISOString() : null,
        duedate: due ? dueDate.toISOString() : null,
        landing_url: landingUrl,
        connection_error: connectionError ? "error" : null,
        parse_error: parseError ? "error" : null,
        release_name: release ? releaseName : null,
        release_released: release ? releaseReleased : null,
        release_date: release ? releaseDate : null,
        sprint_name: sprint ? "Sprint 42" : null,
        sprint_state: sprint ? "active" : null,
        sprint_enddate: sprint ? sprintEndDate : null,
    }
    if (statusCategory) {
        issueStatus["status_category"] = statusCategory
    }
    const metric = {
        issue_ids: ["123"],
        issue_status: [issueStatus],
    }
    return render(<IssueStatus metric={metric} issueTrackerMissing={issueTrackerMissing} settings={settings} />)
}

beforeEach(() => {
    history.push("")
})

it("displays the issue id", () => {
    const { queryByText } = renderIssueStatus()
    expect(queryByText(/123/)).not.toBe(null)
})

it("displays the status", () => {
    const { queryByText } = renderIssueStatus()
    expect(queryByText(/in progress/)).not.toBe(null)
})

it("displays the status category doing", () => {
    renderIssueStatus({ statusCategory: "doing" })
    expect(screen.getByText(/123/).className).toContain("blue")
})

it("displays the status category todo", () => {
    renderIssueStatus({ statusCategory: "todo" })
    expect(screen.getByText(/123/).className).toContain("grey")
})

it("displays the status category done", () => {
    renderIssueStatus({ statusCategory: "done" })
    expect(screen.getByText(/123/).className).toContain("green")
})

it("displays a missing status category as unknown", () => {
    renderIssueStatus()
    Object.values(ISSUE_STATUS_COLORS)
        .filter((color) => color !== null)
        .forEach((color) => {
            expect(screen.getByText(/123/).className).not.toContain(color)
        })
})

it("displays the issue landing url", async () => {
    const { queryByText } = renderIssueStatus()
    expect(queryByText(/123/).closest("a").href).toBe("https://issue/")
})

it("does not display an url if the issue has no landing url", async () => {
    const { queryByText } = renderIssueStatus({ landingUrl: null })
    expect(queryByText(/123/).closest("a")).toBe(null)
})

it("displays a question mark as status if the issue has no status", () => {
    const { queryByText } = renderIssueStatus({ status: null })
    expect(queryByText(/\?/)).not.toBe(null)
})

it("displays the issue summary in the label if configured", async () => {
    history.push("?show_issue_summary=true")
    const { queryByText } = renderIssueStatus()
    expect(queryByText(/summary/)).not.toBe(null)
})

it("displays the creation date in the label if configured", async () => {
    history.push("?show_issue_creation_date=true")
    const { queryByText } = renderIssueStatus()
    expect(queryByText(/4 days ago/)).not.toBe(null)
})

it("does not display the creation date in the label if not configured", async () => {
    const { queryByText } = renderIssueStatus()
    expect(queryByText(/4 days ago/)).toBe(null)
})

it("displays the issue summary in the popup", async () => {
    const { queryByText } = renderIssueStatus()
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText("Issue summary")).not.toBe(null)
    })
})

it("displays the creation date in the popup", async () => {
    const { queryByText } = renderIssueStatus({ updated: false })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText("4 days ago")).not.toBe(null)
        expect(queryByText("2 days ago")).toBe(null)
    })
})

it("displays the update date in the label if configured", async () => {
    history.push("?show_issue_update_date=true")
    const { queryByText } = renderIssueStatus({ updated: true })
    expect(queryByText(/2 days ago/)).not.toBe(null)
})

it("does not display the update date in the label if not configured", async () => {
    const { queryByText } = renderIssueStatus({ updated: true })
    expect(queryByText(/2 days ago/)).toBe(null)
})

it("displays the update date in the popup", async () => {
    const { queryByText } = renderIssueStatus({ updated: true })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText("4 days ago")).not.toBe(null)
        expect(queryByText("2 days ago")).not.toBe(null)
    })
})

it("displays the due date in the label if configured", async () => {
    history.push("?show_issue_due_date=true")
    const { queryByText } = renderIssueStatus({ due: true })
    expect(queryByText(/2 days from now/)).not.toBe(null)
})

it("does not display the due date in the label if not configured", async () => {
    const { queryByText } = renderIssueStatus({ due: true })
    expect(queryByText(/2 days from now/)).toBe(null)
})

it("displays the due date in the popup", async () => {
    const { queryByText } = renderIssueStatus({ due: true })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText("2 days from now")).not.toBe(null)
    })
})

it("displays the planned release in the label if configured", async () => {
    history.push("?show_issue_release=true")
    const { queryByText } = renderIssueStatus({ release: true })
    expect(queryByText(/1.0/)).not.toBe(null)
    expect(queryByText(/planned/)).not.toBe(null)
    expect(queryByText(/from now/)).not.toBe(null)
})

it("displays the released release in the label if configured", async () => {
    history.push("?show_issue_release=true")
    const { queryByText } = renderIssueStatus({ release: true, releaseReleased: true })
    expect(queryByText(/1.0/)).not.toBe(null)
    expect(queryByText(/released/)).not.toBe(null)
    expect(queryByText(/from now/)).not.toBe(null)
})

it("displays the release in the label if configured, but without release date", async () => {
    history.push("?show_issue_release=true")
    const { queryByText } = renderIssueStatus({ release: true, releaseDate: null })
    expect(queryByText(/1.0/)).not.toBe(null)
    expect(queryByText(/planned/)).not.toBe(null)
    expect(queryByText(/from now/)).toBe(null)
})

it("displays the release without doubling release in the label", async () => {
    history.push("?show_issue_release=true")
    const { queryByText } = renderIssueStatus({ release: true, releaseName: "Release 1.0" })
    expect(queryByText(/Release 1.0/)).not.toBe(null)
    expect(queryByText(/Release Release 1.0/)).toBe(null)
})

it("does not display the release in the label if not configured", async () => {
    const { queryByText } = renderIssueStatus({ release: true })
    expect(queryByText(/1.0/)).toBe(null)
    expect(queryByText(/planned/)).toBe(null)
    expect(queryByText(/from now/)).toBe(null)
})

it("displays the release in the popup", async () => {
    const { queryByText } = renderIssueStatus({ release: true })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText(/1.0/)).not.toBe(null)
        expect(queryByText(/planned/)).not.toBe(null)
        expect(queryByText(/from now/)).not.toBe(null)
    })
})

it("displays the release in the popup without release date", async () => {
    const { queryByText } = renderIssueStatus({ release: true, releaseDate: null })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText(/1.0/)).not.toBe(null)
        expect(queryByText(/planned/)).toBe(null)
        expect(queryByText(/from now/)).toBe(null)
    })
})

it("displays the sprint in the label if configured", async () => {
    history.push("?show_issue_sprint=true")
    const { queryByText } = renderIssueStatus({ sprint: true })
    expect(queryByText(/Sprint 42/)).not.toBe(null)
    expect(queryByText(/active/)).not.toBe(null)
    expect(queryByText(/from now/)).not.toBe(null)
})

it("displays the sprint in the label if configured, but without sprint end date", async () => {
    history.push("?show_issue_sprint=true")
    const { queryByText } = renderIssueStatus({ sprint: true, sprintEndDate: null })
    expect(queryByText(/Sprint 42/)).not.toBe(null)
    expect(queryByText(/active/)).not.toBe(null)
    expect(queryByText(/from now/)).toBe(null)
})

it("does not display the sprint in the label if not configured", async () => {
    const { queryByText } = renderIssueStatus({ sprint: true })
    expect(queryByText(/Sprint 42/)).toBe(null)
    expect(queryByText(/active/)).toBe(null)
    expect(queryByText(/from now/)).toBe(null)
})

it("displays the sprint in the popup", async () => {
    const { queryByText } = renderIssueStatus({ sprint: true })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText(/Sprint 42/)).not.toBe(null)
        expect(queryByText(/active/)).not.toBe(null)
        expect(queryByText(/from now/)).not.toBe(null)
    })
})

it("displays the sprint in the popup without sprint end date", async () => {
    const { queryByText } = renderIssueStatus({ sprint: true, sprintEndDate: null })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText(/Sprint 42/)).not.toBe(null)
        expect(queryByText(/active/)).not.toBe(null)
        expect(queryByText(/from now/)).toBe(null)
    })
})

it("displays no popup if the issue has no creation date and there is no error", async () => {
    const { queryByText } = renderIssueStatus({ created: false })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText("4 days ago")).toBe(null)
        expect(queryByText("2 days ago")).toBe(null)
        expect(queryByText("2 days from now")).toBe(null)
    })
})

it("displays a connection error in the popup", async () => {
    const { queryByText } = renderIssueStatus({ connectionError: true })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText("Connection error")).not.toBe(null)
    })
})

it("displays a parse error in the popup", async () => {
    const { queryByText } = renderIssueStatus({ parseError: true })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText("Parse error")).not.toBe(null)
    })
})

it("displays nothing if the metric has no issue status", async () => {
    const { queryByText } = render(<IssueStatus metric={{}} />)
    expect(queryByText(/123/)).toBe(null)
})

it("displays an error message if the metric has issue ids but the report has no issue tracker", async () => {
    const { queryByText } = renderIssueStatus({ issueTrackerMissing: true })
    await userEvent.hover(queryByText(/123/))
    await waitFor(() => {
        expect(queryByText(/No issue tracker configured/)).not.toBe(null)
    })
})