Files
website/client/test/teamActivity.test.js
wtclaude 8012a457fc
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 39s
PR Checks / client-build (pull_request) Successful in 58s
PR Checks / server-tests (pull_request) Successful in 12m43s
fix(teams): say "game server", not "shard", on the never-synced Teams panel
Admin → Teams told an operator that an unsynced projection "is not a
confirmed empty shard". Teams is a core primitive and core's word for the
host is the game server; "shard" is Ultima Online's. The detail now reads
"What is shown below does not confirm that the game server has no Teams."

The matching test, and the four comments that said "shard" about the same
state, follow it. Found while making runicgateway.com's core docs
game-neutral (runicgateway.com#34), which had to paraphrase this line
rather than quote it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-22 14:43:37 -05:00

79 lines
3.4 KiB
JavaScript

// What core's Team activity feed says (client/src/lib/teamActivity.js).
//
// The test that earns this file: a projection nobody can tell is stale, and a
// feed nobody can tell is filtered, both look like complete information. Every
// case below is about saying which one the reader is looking at.
//
// Note the wording assertions avoid core's own noun. The feed renders inside a
// page a MODULE titled — Guilds today, Clans next — so "this Team" would be
// core's vocabulary leaking onto a surface that deliberately does not use it.
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { activityScopeNote, freshnessNote, groupByDay, relativeTime } from '../src/lib/teamActivity.js'
const NOW = new Date('2026-08-17T12:00:00Z').getTime()
const ago = (ms) => new Date(NOW - ms).toISOString()
test('a deployment with no provider is not stale, it is uninvolved', () => {
assert.equal(freshnessNote({ configured: false }, NOW), null)
})
test('never synced is a warning, and never reads as a confirmed empty roster', () => {
const note = freshnessNote({ configured: true, lastSyncAt: null }, NOW)
assert.equal(note.tone, 'warn')
assert.match(note.text, /Not yet confirmed/)
})
test('a stale projection says how old it is and that the game may have moved on', () => {
const note = freshnessNote({ configured: true, lastSyncAt: ago(14 * 60_000), stale: true }, NOW)
assert.equal(note.tone, 'warn')
assert.equal(note.text, 'Last confirmed 14 minutes ago — the game may have moved on.')
})
test('a current projection is stated quietly', () => {
const note = freshnessNote({ configured: true, lastSyncAt: ago(90_000), stale: false }, NOW)
assert.equal(note.tone, 'idle')
assert.equal(note.text, 'Last confirmed 1 minute ago.')
})
test('relative time singularises and steps through the units', () => {
assert.equal(relativeTime(ago(5_000), NOW), 'just now')
assert.equal(relativeTime(ago(60_000), NOW), '1 minute ago')
assert.equal(relativeTime(ago(3 * 3_600_000), NOW), '3 hours ago')
assert.equal(relativeTime(ago(2 * 86_400_000), NOW), '2 days ago')
assert.equal(relativeTime(null, NOW), null)
assert.equal(relativeTime('not a date', NOW), null)
})
test('items group into days, newest day first, order kept within a day', () => {
const days = groupByDay([
{ id: 3, occurredAt: '2026-08-17T09:00:00' },
{ id: 2, occurredAt: '2026-08-17T08:00:00' },
{ id: 1, occurredAt: '2026-08-16T22:00:00' },
], 'en-US')
assert.equal(days.length, 2)
assert.deepEqual(days[0].items.map((i) => i.id), [3, 2])
assert.deepEqual(days[1].items.map((i) => i.id), [1])
})
test('an unparseable timestamp is skipped rather than making a day called Invalid Date', () => {
assert.deepEqual(groupByDay([{ id: 1, occurredAt: 'nonsense' }], 'en-US'), [])
})
test('a caller who saw everything is told nothing', () => {
assert.equal(activityScopeNote({ scope: 'members' }, true), null)
})
test('a filtered feed says so, and invites an anonymous caller to sign in', () => {
assert.match(activityScopeNote({ scope: 'public' }, false), /Sign in/)
assert.match(activityScopeNote({ scope: 'public' }, true), /members only/)
})
test('the wording never says "Team" — that is core\'s noun, not the page\'s', () => {
for (const signedIn of [true, false]) {
assert.doesNotMatch(activityScopeNote({ scope: 'public' }, signedIn), /Team/)
}
assert.doesNotMatch(freshnessNote({ configured: true, lastSyncAt: null }, NOW).text, /Team/)
})