1- import { describe , it , expect , vi } from "vitest" ;
1+ import { describe , it , expect , vi , beforeEach , afterEach } from "vitest" ;
22import { render , screen } from "@solidjs/testing-library" ;
33import userEvent from "@testing-library/user-event" ;
4+ import { createSignal } from "solid-js" ;
45import WorkflowRunRow from "../../src/app/components/dashboard/WorkflowRunRow" ;
56import { makeWorkflowRun } from "../helpers/index" ;
67
8+ const MOCK_NOW = new Date ( "2026-03-30T12:00:00Z" ) . getTime ( ) ;
9+
710describe ( "WorkflowRunRow" , ( ) => {
11+ beforeEach ( ( ) => { vi . spyOn ( Date , "now" ) . mockReturnValue ( MOCK_NOW ) ; } ) ;
12+ afterEach ( ( ) => { vi . restoreAllMocks ( ) ; } ) ;
13+
814 it ( "renders run name" , ( ) => {
915 const run = makeWorkflowRun ( { name : "CI Build" } ) ;
1016 render ( ( ) => (
@@ -21,15 +27,32 @@ describe("WorkflowRunRow", () => {
2127 screen . getByText ( "feat: my cool feature" ) ;
2228 } ) ;
2329
24- it ( "shows relative time" , ( ) => {
25- const run = makeWorkflowRun ( { createdAt : "2024-01-10T08:00:00Z" } ) ;
26- render ( ( ) => (
30+ it ( "shows relative time in a semantic <time> element" , ( ) => {
31+ const createdAt = new Date ( MOCK_NOW - 2 * 60 * 60 * 1000 ) . toISOString ( ) ;
32+ const run = makeWorkflowRun ( { createdAt } ) ;
33+ const { container } = render ( ( ) => (
2734 < WorkflowRunRow run = { run } onIgnore = { ( ) => { } } density = "comfortable" />
2835 ) ) ;
29- // relativeTime returns a human-readable string; just verify something renders
30- // We can't assert exact text as it depends on current date, but the element should exist
31- const container = screen . getByText ( "CI" ) . closest ( "div" ) ;
32- expect ( container ) . toBeDefined ( ) ;
36+ const timeEl = container . querySelector ( "time" ) ;
37+ expect ( timeEl ) . not . toBeNull ( ) ;
38+ expect ( timeEl ! . getAttribute ( "datetime" ) ) . toBe ( createdAt ) ;
39+ expect ( timeEl ! . textContent ) . toMatch ( / 2 h o u r s ? a g o / ) ;
40+ } ) ;
41+
42+ it ( "updates time display when refreshTick changes" , ( ) => {
43+ let mockNow = MOCK_NOW ;
44+ vi . spyOn ( Date , "now" ) . mockImplementation ( ( ) => mockNow ) ;
45+ const createdAt = new Date ( MOCK_NOW - 2 * 60 * 60 * 1000 ) . toISOString ( ) ;
46+ const run = makeWorkflowRun ( { createdAt } ) ;
47+ const [ tick , setTick ] = createSignal ( 0 ) ;
48+ const { container } = render ( ( ) => (
49+ < WorkflowRunRow run = { run } onIgnore = { ( ) => { } } density = "comfortable" refreshTick = { tick ( ) } />
50+ ) ) ;
51+ const timeEl = container . querySelector ( "time" ) ;
52+ expect ( timeEl ! . textContent ) . toMatch ( / 2 h o u r s ? a g o / ) ;
53+ mockNow = MOCK_NOW + 3 * 60 * 60 * 1000 ;
54+ setTick ( 1 ) ;
55+ expect ( timeEl ! . textContent ) . toMatch ( / 5 h o u r s ? a g o / ) ;
3356 } ) ;
3457
3558 it ( "renders status indicator for success conclusion" , ( ) => {
0 commit comments