analytics / assets /js /dashboard /annotations /annotations.test.ts
Leon4gr45's picture
Upload folder using huggingface_hub
b2a00c5 verified
Raw
History Blame Contribute Delete
8.75 kB
import {
AnnotationGranularity,
AnnotationType,
canAddAnnotation,
canEditAnnotation,
getAnnotationAuthorship,
getAnnotationGranularity,
getAnnotationTimeLabel,
groupAnnotationsByTimeLabel
} from './annotations'
import { Interval } from '../stats/graph/intervals'
import { Role, UserContextValue } from '../user-context'
const loggedInUser = (role: Role, id: number = 42): UserContextValue => ({
loggedIn: true,
id,
role,
team: { identifier: null, hasConsolidatedView: false }
})
const publicUser: UserContextValue = {
loggedIn: false,
id: null,
role: Role.public,
team: { identifier: null, hasConsolidatedView: false }
}
describe(`${getAnnotationAuthorship.name}`, () => {
it('returns the owner name for a site annotation with an owner', () => {
expect(
getAnnotationAuthorship({
type: AnnotationType.site,
owner_name: 'Alice'
})
).toBe('Alice')
})
it('returns "Site note" for a site annotation with a dangling owner', () => {
expect(
getAnnotationAuthorship({
type: AnnotationType.site,
owner_name: null
})
).toBe('Site note')
})
it('returns "Personal note" for a personal annotation regardless of owner (we assume personal notes are served only to the author)', () => {
expect(
getAnnotationAuthorship({
type: AnnotationType.personal,
owner_name: 'Alice'
})
).toBe('Personal note')
})
})
describe(`${canAddAnnotation.name}`, () => {
it.each([[Role.admin], [Role.editor], [Role.owner]])(
'allows adding a site annotation if the user is logged in, in role %p, and the site-annotations feature is available',
(role) => {
expect(
canAddAnnotation({
type: AnnotationType.site,
user: loggedInUser(role),
siteAnnotationsAvailable: true
})
).toBe(true)
}
)
it.each([[Role.admin], [Role.editor], [Role.owner]])(
'forbids adding a site annotation in role %p when the feature is unavailable (billing gate)',
(role) => {
expect(
canAddAnnotation({
type: AnnotationType.site,
user: loggedInUser(role),
siteAnnotationsAvailable: false
})
).toBe(false)
}
)
it.each([[Role.viewer], [Role.billing]])(
'forbids adding a site annotation in role %p even when the feature is available',
(role) => {
expect(
canAddAnnotation({
type: AnnotationType.site,
user: loggedInUser(role),
siteAnnotationsAvailable: true
})
).toBe(false)
}
)
it.each([
[Role.viewer],
[Role.billing],
[Role.editor],
[Role.admin],
[Role.owner]
])(
'allows adding a personal annotation in role %p regardless of site-annotations availability',
(role) => {
expect(
canAddAnnotation({
type: AnnotationType.personal,
user: loggedInUser(role),
siteAnnotationsAvailable: true
})
).toBe(true)
expect(
canAddAnnotation({
type: AnnotationType.personal,
user: loggedInUser(role),
siteAnnotationsAvailable: false
})
).toBe(true)
}
)
it.each([[AnnotationType.personal], [AnnotationType.site]])(
'forbids the public role from adding %s annotations',
(type) => {
expect(
canAddAnnotation({
type,
user: publicUser,
siteAnnotationsAvailable: true
})
).toBe(false)
}
)
})
describe(`${canEditAnnotation.name}`, () => {
it.each([[Role.admin], [Role.editor], [Role.owner]])(
'allows editing a site annotation if the user is logged in and in role %p',
(role) => {
expect(
canEditAnnotation({
type: AnnotationType.site,
user: loggedInUser(role)
})
).toBe(true)
}
)
it.each([[Role.viewer], [Role.billing]])(
'forbids editing site annotations in role %p',
(role) => {
expect(
canEditAnnotation({
type: AnnotationType.site,
user: loggedInUser(role)
})
).toBe(false)
}
)
it.each([
[Role.viewer],
[Role.billing],
[Role.editor],
[Role.admin],
[Role.owner]
])('allows editing a personal annotation when in role %p', (role) => {
expect(
canEditAnnotation({
type: AnnotationType.personal,
user: loggedInUser(role)
})
).toBe(true)
})
it.each([[AnnotationType.personal], [AnnotationType.site]])(
'forbids the public role from editing %s annotations',
(type) => {
expect(
canEditAnnotation({
type,
user: publicUser
})
).toBe(false)
}
)
})
describe(`${getAnnotationGranularity.name}`, () => {
it.each<[Interval, AnnotationGranularity]>([
[Interval.minute, AnnotationGranularity.minute],
[Interval.hour, AnnotationGranularity.minute],
[Interval.day, AnnotationGranularity.date],
[Interval.week, AnnotationGranularity.date],
[Interval.month, AnnotationGranularity.date]
])('maps interval %s to granularity %s', (interval, granularity) => {
expect(getAnnotationGranularity(interval)).toBe(granularity)
})
})
describe(`${getAnnotationTimeLabel.name}`, () => {
// 2025-02-26 is a Wednesday
const dateAnnotation = {
datetime: '2025-02-26',
granularity: AnnotationGranularity.date
}
it.each<[Interval, string]>([
[Interval.minute, '2025-02-26'],
[Interval.hour, '2025-02-26'],
[Interval.day, '2025-02-26'],
[Interval.week, '2025-02-24'],
[Interval.month, '2025-02-01']
])(
`date-granularity annotation on ${dateAnnotation.datetime} bucketed to %s yields %s`,
(interval, expected) => {
expect(getAnnotationTimeLabel(dateAnnotation, interval)).toBe(expected)
}
)
const minuteAnnotation = {
datetime: '2025-02-26T10:30:00',
granularity: AnnotationGranularity.minute
}
it.each<[Interval, string]>([
[Interval.month, '2025-02-01'],
[Interval.week, '2025-02-24'],
[Interval.day, '2025-02-26'],
[Interval.hour, '2025-02-26 10:00:00'],
[Interval.minute, '2025-02-26 10:30:00']
])(
`minute granularity annotation with datetime ${minuteAnnotation.datetime} bucketed to %s yields %s`,
(interval, expected) => {
expect(getAnnotationTimeLabel(minuteAnnotation, interval)).toBe(expected)
}
)
})
describe(`${groupAnnotationsByTimeLabel.name}`, () => {
const dateGranularity = AnnotationGranularity.date
const annotations = [
{ id: 1, datetime: '2025-02-24 00:00:00', granularity: dateGranularity }, // Mon
{ id: 2, datetime: '2025-02-26 00:00:00', granularity: dateGranularity }, // Wed (same week)
{ id: 3, datetime: '2025-03-05 00:00:00', granularity: dateGranularity } // following month
]
it('groups annotations by day when the interval is day', () => {
const grouped = groupAnnotationsByTimeLabel(annotations, Interval.day)
expect(Object.keys(grouped).sort()).toEqual([
'2025-02-24',
'2025-02-26',
'2025-03-05'
])
expect(grouped['2025-02-24']!.map((a) => a.id)).toEqual([1])
expect(grouped['2025-02-26']!.map((a) => a.id)).toEqual([2])
expect(grouped['2025-03-05']!.map((a) => a.id)).toEqual([3])
})
it('collapses annotations from the same week into one bucket', () => {
const grouped = groupAnnotationsByTimeLabel(annotations, Interval.week)
expect(Object.keys(grouped).sort()).toEqual(['2025-02-24', '2025-03-03'])
expect(grouped['2025-02-24']!.map((a) => a.id)).toEqual([1, 2])
expect(grouped['2025-03-03']!.map((a) => a.id)).toEqual([3])
})
it('collapses annotations from the same month into one bucket', () => {
const grouped = groupAnnotationsByTimeLabel(annotations, Interval.month)
expect(Object.keys(grouped).sort()).toEqual(['2025-02-01', '2025-03-01'])
expect(grouped['2025-02-01']!.map((a) => a.id)).toEqual([1, 2])
expect(grouped['2025-03-01']!.map((a) => a.id)).toEqual([3])
})
it('preserves insertion order within a bucket', () => {
const sameDay = [
{ id: 10, datetime: '2025-02-26 08:00:00', granularity: dateGranularity },
{ id: 11, datetime: '2025-02-26 09:00:00', granularity: dateGranularity },
{ id: 12, datetime: '2025-02-26 10:00:00', granularity: dateGranularity }
]
const grouped = groupAnnotationsByTimeLabel(sameDay, Interval.day)
expect(grouped['2025-02-26']!.map((a) => a.id)).toEqual([10, 11, 12])
})
it('returns an empty object when there are no annotations', () => {
expect(
groupAnnotationsByTimeLabel(
[] as { datetime: string; granularity: AnnotationGranularity }[],
Interval.day
)
).toEqual({})
})
})