components/frontend/src/utils.test.js
import { EDIT_ENTITY_PERMISSION, EDIT_REPORT_PERMISSION } from "./context/Permissions"
import { defaultDesiredResponseTimes } from "./defaults"
import {
addCounts,
capitalize,
getMetricResponseDeadline,
getMetricResponseOverrun,
getMetricTags,
getMetricTarget,
getReportTags,
getSourceName,
getSubjectName,
getSubjectType,
getSubjectTypeMetrics,
getUserPermissions,
isMeasurementOutdated,
isMeasurementRequested,
isMeasurementStale,
limitTextLength,
niceNumber,
nrMetricsInReport,
nrMetricsInReports,
scaledNumber,
sortWithLocaleCompare,
sum,
userPrefersDarkMode,
visibleMetrics,
} from "./utils"
const dataModel = {
metrics: {
metric_type: {
default_scale: "count",
},
},
}
const metric = {
type: "metric_type",
}
it("capitalizes strings", () => {
expect(capitalize("")).toBe("")
expect(capitalize("A")).toBe("A")
expect(capitalize("a")).toBe("A")
expect(capitalize("ab")).toBe("Ab")
expect(capitalize("aB")).toBe("AB")
expect(capitalize("AB")).toBe("AB")
expect(capitalize("a_b")).toBe("A b")
})
it("rounds numbers nicely", () => {
expect(niceNumber(0)).toBe(10)
expect(niceNumber(1)).toBe(10)
expect(niceNumber(9)).toBe(10)
expect(niceNumber(10)).toBe(12)
expect(niceNumber(12)).toBe(15)
expect(niceNumber(15)).toBe(20)
expect(niceNumber(16)).toBe(20)
expect(niceNumber(17)).toBe(20)
expect(niceNumber(39)).toBe(50)
expect(niceNumber(40)).toBe(50)
expect(niceNumber(41)).toBe(50)
expect(niceNumber(79)).toBe(100)
expect(niceNumber(80)).toBe(100)
expect(niceNumber(81)).toBe(100)
expect(niceNumber(90)).toBe(100)
expect(niceNumber(100)).toBe(120)
expect(niceNumber(125)).toBe(150)
})
it("adds a scale", () => {
expect(scaledNumber(1)).toBe("1")
expect(scaledNumber(12)).toBe("12")
expect(scaledNumber(123)).toBe("123")
expect(scaledNumber(1234)).toBe("1k")
expect(scaledNumber(12345)).toBe("12k")
expect(scaledNumber(123456)).toBe("123k")
expect(scaledNumber(1234567)).toBe("1m")
expect(scaledNumber(12345678)).toBe("12m")
expect(scaledNumber(123456789)).toBe("123m")
})
it("gives users all permissions if permissions have not been limited", () => {
const permissions = getUserPermissions("jodoe", "john.doe@example.org", null, {})
expect(permissions).toStrictEqual([EDIT_REPORT_PERMISSION, EDIT_ENTITY_PERMISSION])
})
it("gives users edit report permissions if edit report permissions have been granted", () => {
const permissions = getUserPermissions("jodoe", "john.doe@example.org", null, {
[EDIT_REPORT_PERMISSION]: ["jodoe"],
[EDIT_ENTITY_PERMISSION]: ["jadoe"],
})
expect(permissions).toStrictEqual([EDIT_REPORT_PERMISSION])
})
it("gives users edit entity permissions if edit entity permissions have been granted", () => {
const permissions = getUserPermissions("jodoe", "john.doe@example.org", null, {
[EDIT_REPORT_PERMISSION]: ["jadoe"],
[EDIT_ENTITY_PERMISSION]: ["jodoe"],
})
expect(permissions).toStrictEqual([EDIT_ENTITY_PERMISSION])
})
it("gives users no permissions if they have not logged in", () => {
const permissions = getUserPermissions(null, null, null, {})
expect(permissions).toStrictEqual([])
})
it("gives users no permissions if the report date is in the past", () => {
const permissions = getUserPermissions("jodoe", "john.doe@example.org", new Date(), {})
expect(permissions).toStrictEqual([])
})
it("gets the metric tags sorted", () => {
expect(getMetricTags({ tags: ["foo", "bar"] })).toStrictEqual(["bar", "foo"])
})
it("gets the metric tags even if there are none", () => {
expect(getMetricTags({})).toStrictEqual([])
})
it("gets the metric target", () => {
expect(getMetricTarget({ target: "2" })).toStrictEqual("2")
})
it("gets the metric target, even if the target is missing", () => {
expect(getMetricTarget({})).toStrictEqual("0")
})
it("gets the source name", () => {
expect(getSourceName({ name: "source" }, {})).toStrictEqual("source")
})
it("gets the source name from the data model if the source has no name", () => {
expect(getSourceName({ type: "source_type" }, { sources: { source_type: { name: "source" } } })).toStrictEqual(
"source",
)
})
it("gets the subject type", () => {
const subject = { name: "Subject" }
expect(getSubjectType("subject", { subject: subject })).toStrictEqual({ name: "Subject" })
})
it("gets the subject type recursively", () => {
const subject = { name: "Subject" }
expect(getSubjectType("subject", { parent: { subjects: { subject: subject } } })).toStrictEqual({
name: "Subject",
})
})
it("gets the subject type recursively from the second subject type", () => {
const subject = { name: "Subject" }
expect(
getSubjectType("subject", {
first: { subjects: { foo: { name: "Foo" } } },
second: { subjects: { subject: subject } },
}),
).toStrictEqual(subject)
})
it("gets the subject type metrics", () => {
expect(getSubjectTypeMetrics("subject", { subject: { metrics: ["metric"] } })).toStrictEqual(["metric"])
})
it("gets the subject type metrics recursively", () => {
const metrics = getSubjectTypeMetrics("child", {
parent: { metrics: ["parent metric"], subjects: { child: { metrics: ["child metric"] } } },
})
expect(metrics).toStrictEqual(["child metric"])
})
it("gets the subject type metrics recursively, including child metrics", () => {
const metrics = getSubjectTypeMetrics("parent", {
parent: { metrics: ["parent metric"], subjects: { child: { metrics: ["child metric"] } } },
})
expect(metrics).toStrictEqual(["parent metric", "child metric"])
})
it("gets the subject type metrics recursively from the second subject type", () => {
const metrics = getSubjectTypeMetrics("child", {
first: { subjects: { foo: { metrics: ["foo metric"] } } },
second: { metrics: ["second metric"], subjects: { child: { metrics: ["child metric"] } } },
})
expect(metrics).toStrictEqual(["child metric"])
})
it("gets the subject type metrics deduplicated", () => {
const metrics = getSubjectTypeMetrics("parent", {
parent: { subjects: { child1: { metrics: ["child metric"] }, child2: { metrics: ["child metric"] } } },
})
expect(metrics).toStrictEqual(["child metric"])
})
it("gets the subject name", () => {
expect(getSubjectName({ name: "subject" }, {})).toStrictEqual("subject")
})
it("gets the subject name from the data model if the subject has no name", () => {
expect(getSubjectName({ type: "subject_type" }, { subjects: { subject_type: { name: "subject" } } })).toStrictEqual(
"subject",
)
})
it("returns true when the user sets dark mode", () => {
expect(userPrefersDarkMode("dark")).toBe(true)
})
it("returns false when the user sets light mode", () => {
expect(userPrefersDarkMode("light")).toBe(false)
})
function mockMatchMedia(matches, addEventListener) {
Object.defineProperty(window, "matchMedia", {
value: jest.fn().mockImplementation((_query) => ({
matches: matches ?? false,
addEventListener: addEventListener ?? jest.fn(),
removeEventListener: jest.fn(),
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
})),
configurable: true,
})
}
it("returns true when the user prefers dark mode", () => {
mockMatchMedia(true)
expect(userPrefersDarkMode("system")).toBe(true)
})
it("returns false when the user prefers light mode", () => {
mockMatchMedia(false)
expect(userPrefersDarkMode("system")).toBe(false)
})
it("returns the metric response deadline", () => {
expect(getMetricResponseDeadline({}, {})).toStrictEqual(null)
})
it("returns the metric response deadline based on the tech debt end date", () => {
const tomorrow = new Date()
tomorrow.setDate(tomorrow.getDate() + 1)
expect(
getMetricResponseDeadline({ status: "debt_target_met", debt_end_date: tomorrow.toISOString() }, {}),
).toStrictEqual(tomorrow)
})
it("returns the metric response deadline based on the desired response time", () => {
const statusStart = "2024-01-01"
const expectedDeadline = new Date(statusStart)
expectedDeadline.setDate(expectedDeadline.getDate() + defaultDesiredResponseTimes.near_target_met)
expect(getMetricResponseDeadline({ status: "near_target_met", status_start: statusStart }, {})).toStrictEqual(
expectedDeadline,
)
})
it("does not return a metric response deadline when the report has been configured not to have a desired response time", () => {
const report = { desired_response_times: { near_target_met: "" } }
expect(getMetricResponseDeadline({ status: "near_target_met", status_start: "2024-02-02" }, report)).toStrictEqual(
null,
)
})
it("returns the metric response overrun when there are no measurements", () => {
expect(getMetricResponseOverrun("uuid", metric, {}, [], dataModel)).toStrictEqual({
overruns: [],
totalOverrun: 0,
})
})
it("returns the metric response overrun when there is no overrun", () => {
const measurements = [{ metric_uuid: "uuid", start: "2000-01-01", end: "2000-01-04" }]
expect(getMetricResponseOverrun("uuid", metric, {}, measurements, dataModel)).toStrictEqual({
overruns: [],
totalOverrun: 0,
})
})
it("returns the metric response overrun when there is one long measurement", () => {
const measurements = [{ metric_uuid: "uuid", start: "2000-01-01", end: "2000-01-31" }]
expect(getMetricResponseOverrun("uuid", metric, {}, measurements, dataModel)).toStrictEqual({
overruns: [
{
status: "unknown",
start: "2000-01-01",
end: "2000-01-31",
actual_response_time: 30,
desired_response_time: 3,
overrun: 27,
},
],
totalOverrun: 27,
})
})
it("returns the metric response overrun when there is one long measurement and the report has no desired response times", () => {
const report = { desired_response_times: { unknown: "" } }
const measurements = [{ metric_uuid: "uuid", start: "2000-01-01", end: "2000-01-31" }]
expect(getMetricResponseOverrun("uuid", metric, report, measurements, dataModel)).toStrictEqual({
overruns: [],
totalOverrun: 0,
})
})
it("returns the metric response overrun when there is one long measurement and the report has desired response times", () => {
const report = { desired_response_times: { unknown: "10" } }
const measurements = [{ metric_uuid: "uuid", start: "2000-01-01", end: "2000-01-31" }]
expect(getMetricResponseOverrun("uuid", metric, report, measurements, dataModel)).toStrictEqual({
overruns: [
{
status: "unknown",
start: "2000-01-01",
end: "2000-01-31",
actual_response_time: 30,
desired_response_time: 10,
overrun: 20,
},
],
totalOverrun: 20,
})
})
it("returns the metric response overrun when the metric status is target met", () => {
const measurements = [
{
metric_uuid: "uuid",
start: "2000-01-01",
end: "2000-01-31",
count: { status: "target_met" },
},
]
expect(getMetricResponseOverrun("uuid", metric, {}, measurements, dataModel)).toStrictEqual({
overruns: [],
totalOverrun: 0,
})
})
it("returns the metric response overrun when the metric status is target not met", () => {
const measurements = [
{
metric_uuid: "uuid",
start: "2000-01-01",
end: "2000-01-31",
count: { status: "target_not_met" },
},
]
expect(getMetricResponseOverrun("uuid", metric, {}, measurements, dataModel)).toStrictEqual({
overruns: [
{
status: "target_not_met",
start: "2000-01-01",
end: "2000-01-31",
actual_response_time: 30,
desired_response_time: 7,
overrun: 23,
},
],
totalOverrun: 23,
})
})
it("returns the metric response overrun when there are two consecutive measurements that together overrun", () => {
const measurements = [
{ metric_uuid: "uuid", start: "2000-01-01", end: "2000-01-03" },
{ metric_uuid: "uuid", start: "2000-01-03", end: "2000-01-05" },
]
expect(getMetricResponseOverrun("uuid", metric, {}, measurements, dataModel)).toStrictEqual({
overruns: [
{
status: "unknown",
start: "2000-01-01",
end: "2000-01-05",
actual_response_time: 4,
desired_response_time: 3,
overrun: 1,
},
],
totalOverrun: 1,
})
})
it("returns the metric response overrun when there are two measurements with different statuses", () => {
const measurements = [
{ metric_uuid: "uuid", start: "2000-01-01", end: "2000-01-03" },
{
metric_uuid: "uuid",
start: "2000-01-03",
end: "2000-01-05",
count: { status: "target_met" },
},
]
expect(getMetricResponseOverrun("uuid", metric, {}, measurements, dataModel)).toStrictEqual({
overruns: [],
totalOverrun: 0,
})
})
it("returns the tags of an empty report", () => {
expect(getReportTags({ subjects: {} })).toStrictEqual([])
})
it("returns the tags of a report with one tag", () => {
expect(
getReportTags({
subjects: { subject_uud: { metrics: { metric_uuid: { tags: ["tag"] } } } },
}),
).toStrictEqual(["tag"])
})
it("does not return hidden tags", () => {
expect(
getReportTags(
{
subjects: {
subject_uud: { metrics: { metric_uuid: { tags: ["tag", "hidden"] } } },
},
},
["hidden"],
),
).toStrictEqual(["tag"])
})
it("hides metrics not requiring action or without issues", () => {
expect(visibleMetrics({}, "no_action_required", [])).toStrictEqual({})
expect(visibleMetrics({}, "no_issues", [])).toStrictEqual({})
expect(visibleMetrics({}, "all", [])).toStrictEqual({})
expect(visibleMetrics({}, "none", [])).toStrictEqual({})
const metricNotRequiringAction = { metric_uuid: { status: "informative" } }
expect(visibleMetrics(metricNotRequiringAction, "no_action_required", [])).toStrictEqual({})
expect(visibleMetrics(metricNotRequiringAction, "no_issues", [])).toStrictEqual({})
expect(visibleMetrics(metricNotRequiringAction, "all", [])).toStrictEqual({})
expect(visibleMetrics(metricNotRequiringAction, "none", [])).toStrictEqual(metricNotRequiringAction)
const metricRequiringAction = { metric_uuid: { status: "target_not_met" } }
expect(visibleMetrics(metricRequiringAction, "no_action_required", [])).toStrictEqual(metricRequiringAction)
expect(visibleMetrics(metricRequiringAction, "no_issues", [])).toStrictEqual({})
expect(visibleMetrics(metricRequiringAction, "none", [])).toStrictEqual(metricRequiringAction)
expect(visibleMetrics(metricRequiringAction, "all", [])).toStrictEqual({})
const metricWithIssue = { metric_uuid: { status: "target_met", issue_ids: ["ID-1"] } }
expect(visibleMetrics(metricWithIssue, "no_action_required", [])).toStrictEqual({})
expect(visibleMetrics(metricWithIssue, "no_issues", [])).toStrictEqual(metricWithIssue)
expect(visibleMetrics(metricWithIssue, "none", [])).toStrictEqual(metricWithIssue)
expect(visibleMetrics(metricWithIssue, "all", [])).toStrictEqual({})
const metricWithRemovedIssue = { metric_uuid: { status: "target_met", issue_ids: [] } }
expect(visibleMetrics(metricWithRemovedIssue, "no_action_required", [])).toStrictEqual({})
expect(visibleMetrics(metricWithRemovedIssue, "no_issues", [])).toStrictEqual({})
expect(visibleMetrics(metricWithRemovedIssue, "none", [])).toStrictEqual(metricWithRemovedIssue)
expect(visibleMetrics(metricWithRemovedIssue, "all", [])).toStrictEqual({})
})
it("hides metrics with hidden tags", () => {
expect(visibleMetrics({}, false, [])).toStrictEqual({})
expect(visibleMetrics({}, false, ["hidden"])).toStrictEqual({})
const metricWithoutTags = { metric_uuid: { tags: [] } }
expect(visibleMetrics(metricWithoutTags, false, [])).toStrictEqual(metricWithoutTags)
expect(visibleMetrics(metricWithoutTags, false, ["hidden"])).toStrictEqual({})
const metricWithHiddenTag = { metric_uuid: { tags: ["hidden"] } }
expect(visibleMetrics(metricWithHiddenTag, false, [])).toStrictEqual(metricWithHiddenTag)
expect(visibleMetrics(metricWithHiddenTag, false, ["hidden"])).toStrictEqual({})
const metricWithMultipleTags = { metric_uuid: { tags: ["hidden", "maybe hidden"] } }
expect(visibleMetrics(metricWithMultipleTags, false, [])).toStrictEqual(metricWithMultipleTags)
expect(visibleMetrics(metricWithMultipleTags, false, ["hidden"])).toStrictEqual(metricWithMultipleTags)
expect(visibleMetrics(metricWithMultipleTags, false, ["hidden", "maybe hidden"])).toStrictEqual({})
})
it("sorts strings with locale compare", () => {
const strings = ["b", "a", "c"]
sortWithLocaleCompare(strings)
expect(strings).toStrictEqual(["a", "b", "c"])
const emptyArray = []
sortWithLocaleCompare(emptyArray)
expect(emptyArray).toStrictEqual([])
})
it("gets the number of reports in a report", () => {
expect(nrMetricsInReport({ subjects: {} })).toBe(0)
expect(nrMetricsInReport({ subjects: { subject_uuid: { metrics: {} } } })).toBe(0)
expect(nrMetricsInReport({ subjects: { subject_uuid: { metrics: { metric_uuid: {} } } } })).toBe(1)
})
it("gets the number of reports in reports", () => {
expect(nrMetricsInReports([{ subjects: {} }])).toBe(0)
expect(nrMetricsInReports([{ subjects: { subject_uuid: { metrics: { metric_uuid: {} } } } }])).toBe(1)
})
it("adds counts", () => {
expect(addCounts({}, {})).toStrictEqual({})
expect(addCounts({ red: 1 }, { red: 0 })).toStrictEqual({ red: 1 })
expect(addCounts({ red: 1, green: 3 }, { red: 2, green: 3 })).toStrictEqual({ red: 3, green: 6 })
expect(() => addCounts({}, { red: 1 })).toThrow()
})
it("sums numbers", () => {
const array = new Array()
expect(sum(array)).toBe(0)
array.push(1)
expect(sum(array)).toBe(1)
array.push(2)
expect(sum(array)).toBe(3)
expect(sum([])).toBe(0)
expect(sum([1])).toBe(1)
expect(sum([1, 2])).toBe(3)
expect(sum({})).toBe(0)
expect(sum({ a: 1 })).toBe(1)
expect(sum({ a: 1, b: 2 })).toBe(3)
})
it("returns whether a measurement is requested for the metric", () => {
// Measurement is not requested when the metric has no measurement requested timestamp:
expect(isMeasurementRequested({})).toBe(false)
// Measurement is requested when the metric has a measurement requested timestamp and no latest measurement:
expect(isMeasurementRequested({ measurement_requested: "2000-01-01" })).toBe(true)
const latest = { end: "2024-01-01" }
// Measurement is not requested when the measurement requested timestamp is older than the latest measurement:
expect(isMeasurementRequested({ measurement_requested: "2023-01-01", latest_measurement: latest })).toBe(false)
// Measurement is requested when the measurement requested timestamp is newer than the latest measurement:
expect(isMeasurementRequested({ measurement_requested: "2025-01-01", latest_measurement: latest })).toBe(true)
})
it("returns whether a metric's measurement is stale", () => {
// A metric without measurements is not stale:
expect(isMeasurementStale({})).toBe(false)
// A metric with an old measurement and report date 'now' is stale:
expect(isMeasurementStale({ latest_measurement: { end: "2024-09-23" } })).toBe(true)
// A metric with measurement older than the report date is stale:
expect(isMeasurementStale({ latest_measurement: { end: "2024-09-23" } }, new Date("2024-09-24"))).toBe(true)
// A metric with measurement nwwer than the report date is not stale:
expect(isMeasurementStale({ latest_measurement: { end: "2024-09-24" } }, new Date("2024-09-23"))).toBe(false)
})
it("returns whether a metric's measurement is outdated", () => {
// A metric without measurements and without sources is not outdated:
expect(isMeasurementOutdated({})).toBe(false)
// A metric without measurements with an empty collection of sources is not outdated:
expect(isMeasurementOutdated({ sources: {} })).toBe(false)
// A metric without measurements but with a source is outdated:
expect(isMeasurementOutdated({ sources: { source_uuid: {} } })).toBe(true)
// A metric with an up-to-date measurement is not outdated:
expect(isMeasurementOutdated({ latest_measurement: {} })).toBe(false)
// A metric with an outdated measurement is outdated:
expect(isMeasurementOutdated({ latest_measurement: { outdated: true } })).toBe(true)
})
it("limits the text length", () => {
// Short texts are not changed:
expect(limitTextLength("short text")).toStrictEqual("short text")
// Long texts are shortened:
expect(limitTextLength("a very very long text", 10)).toStrictEqual("a very ...")
// Very short texts result in an ellipsis only:
expect(limitTextLength("short text", 4)).toStrictEqual("s...")
expect(limitTextLength("short text", 3)).toStrictEqual("...")
expect(limitTextLength("short text", 2)).toStrictEqual("...")
expect(limitTextLength("short text", 1)).toStrictEqual("...")
expect(limitTextLength("short text", 0)).toStrictEqual("...")
// Arguments that are not text are returned unchanged:
expect(limitTextLength(<>{"short text"}</>)).toStrictEqual(<>{"short text"}</>)
})