Skip to content

Commit b7d1cef

Browse files
authored
Using toLocaleString consistently across diagrams (#2483)
Make the saga diagram use toLocaleString as the rest of the diagrams do
1 parent 2864dcb commit b7d1cef

File tree

4 files changed

+30
-114
lines changed

4 files changed

+30
-114
lines changed

src/Frontend/src/components/messages/SagaDiagram.spec.ts

Lines changed: 26 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface componentDSL {
1414

1515
//Defines a domain-specific language (DSL) for checking assertions against the system under test (sut)
1616
interface componentDSLAssertions {
17-
thereAreTheFollowingSagaChangesInThisOrder(sagaUpdates: { expectedTime: Date }[]): void;
17+
thereAreTheFollowingSagaChangesInThisOrder(expectedDatesInOrder: Date[]): void;
1818
displayedSagaGuidIs(sagaId: string): void;
1919
displayedSagaNameIs(humanizedSagaName: string): void;
2020
linkIsShown(arg0: { withText: string; withHref: string }): void;
@@ -109,8 +109,15 @@ describe("Feature: 3 Visual Representation of Saga Timeline", () => {
109109
});
110110
});
111111

112-
describe("Rule: 3.2 Display a chronological timeline of saga events in UTC.", () => {
113-
test("EXAMPLE: Rendering a Saga with 4 changes", () => {
112+
describe("Rule: 3.3 Display a chronological timeline of saga events localized to user environment.", () => {
113+
test.each([
114+
{
115+
timezone: "UTC",
116+
},
117+
{
118+
timezone: "America/Los_Angeles",
119+
},
120+
])("EXAMPLE: Rendering a Saga with 4 changes - User Timezone $timezone", ({ timezone }) => {
114121
// Each saga event ("Saga Initiated," "Saga Updated," "Timeout Invoked," "Saga Completed") is timestamped to represent progression over time. Events are ordered by the time they ocurred.
115122
//TODO: "Incoming messages are displayed on the left, and outgoing messages are displayed on the right." in another test?
116123

@@ -127,10 +134,10 @@ describe("Feature: 3 Visual Representation of Saga Timeline", () => {
127134

128135
// Set the environment to a fixed timezone
129136
// JSDOM, used by Vitest, defaults to UTC timezone
130-
// To ensure consistency, explicitly set the timezone to UTC
137+
// To ensure consistency, explicitly set the timezone
131138
// This ensures that the rendered local time of the saga changes
132-
// will always be interpreted and displayed in UTC, avoiding flakiness
133-
process.env.TZ = "UTC";
139+
// will always be interpreted and displayed in the specified timezone, avoiding flakiness
140+
process.env.TZ = timezone;
134141

135142
//access each of the saga changes and update its start time and finish time to the same values being read from the variable declaration,
136143
// but set them again explicitly here
@@ -168,92 +175,7 @@ describe("Feature: 3 Visual Representation of Saga Timeline", () => {
168175
});
169176

170177
//assert
171-
componentDriver.assert.thereAreTheFollowingSagaChangesInThisOrder([
172-
{
173-
expectedTime: startTimeD,
174-
},
175-
{
176-
expectedTime: startTimeC,
177-
},
178-
{
179-
expectedTime: startTimeB,
180-
},
181-
{
182-
expectedTime: startTimeA,
183-
},
184-
]);
185-
});
186-
});
187-
describe("Rule: 3.3 Display a chronological timeline of saga events in PST.", () => {
188-
test("EXAMPLE: Rendering a Saga with 4 changes", () => {
189-
// Each saga event ("Saga Initiated," "Saga Updated," "Timeout Invoked," "Saga Completed") is timestamped to represent progression over time. Events are ordered by the time they ocurred.
190-
//TODO: "Incoming messages are displayed on the left, and outgoing messages are displayed on the right." in another test?
191-
192-
//arragement
193-
//sampleSagaHistory already not sorted TODO: Make this more clear so the reader of this test doesn't have to go arround and figure out the preconditions
194-
const messageStore = {} as MessageStore;
195-
messageStore.state = {} as MessageStore["state"];
196-
messageStore.state.data = {} as MessageStore["state"]["data"];
197-
messageStore.state.data.invoked_saga = {
198-
has_saga: true,
199-
saga_id: "123",
200-
saga_type: "ServiceControl.SmokeTest.AuditingSaga",
201-
};
202-
203-
// Set the environment to a fixed timezone
204-
// JSDOM, used by Vitest, defaults to PST timezone
205-
// To ensure consistency, explicitly set the timezone to PST
206-
// This ensures that the rendered local time of the saga changes
207-
// will always be interpreted and displayed in "America/Los_Angeles"
208-
process.env.TZ = "America/Los_Angeles";
209-
210-
//access each of the saga changes and update its start time and finish time to the same values being read from the variable declaration,
211-
// but set them again explicitly here
212-
//so that the reader of this test can see the preconditions at play
213-
//and understand the test better without having to jump around
214-
const startTimeA = new Date("2025-03-28T03:04:08.000Z");
215-
const finishTimeA1 = new Date("2025-03-28T03:04:08.000Z");
216-
const startTimeB = new Date("2025-03-28T03:04:07.000Z");
217-
const finishTimeB1 = new Date("2025-03-28T03:04:07.000Z");
218-
const startTimeC = new Date("2025-03-28T03:04:06.000Z");
219-
const finishTimeC1 = new Date("2025-03-28T03:04:06.000Z");
220-
const startTimeD = new Date("2025-03-28T03:04:05.000Z");
221-
const finishTimeD1 = new Date("2025-03-28T03:04:05.000Z");
222-
223-
sampleSagaHistory.changes[0].start_time = startTimeA;
224-
sampleSagaHistory.changes[0].finish_time = finishTimeA1;
225-
sampleSagaHistory.changes[1].start_time = startTimeB;
226-
sampleSagaHistory.changes[1].finish_time = finishTimeB1;
227-
sampleSagaHistory.changes[2].start_time = startTimeC;
228-
sampleSagaHistory.changes[2].finish_time = finishTimeC1;
229-
sampleSagaHistory.changes[3].start_time = startTimeD;
230-
sampleSagaHistory.changes[3].finish_time = finishTimeD1;
231-
sampleSagaHistory.changes[3].status = "new";
232-
233-
// Set up the store with sample saga history
234-
const componentDriver = rendercomponent({
235-
initialState: {
236-
MessageStore: messageStore,
237-
SagaDiagramStore: { sagaHistory: sampleSagaHistory },
238-
},
239-
});
240-
241-
//assert
242-
243-
componentDriver.assert.thereAreTheFollowingSagaChangesInThisOrder([
244-
{
245-
expectedTime: startTimeD,
246-
},
247-
{
248-
expectedTime: startTimeC,
249-
},
250-
{
251-
expectedTime: startTimeB,
252-
},
253-
{
254-
expectedTime: startTimeA,
255-
},
256-
]);
178+
componentDriver.assert.thereAreTheFollowingSagaChangesInThisOrder([startTimeD, startTimeC, startTimeB, startTimeA]);
257179
});
258180
});
259181
});
@@ -335,30 +257,29 @@ function rendercomponent({ initialState = {} }: { initialState?: { MessageStore?
335257
expect(sagaGuid).toBeInTheDocument();
336258
expect(sagaGuid).toHaveTextContent(guid);
337259
},
338-
thereAreTheFollowingSagaChangesInThisOrder: function (sagaUpdates: { expectedTime: Date }[]): void {
260+
thereAreTheFollowingSagaChangesInThisOrder: function (expectedDatesInOrder: Date[]): void {
339261
//Retrive the main parent component that contains the saga changes
340262
const sagaChangesContainer = screen.getByRole("table", { name: /saga-sequence-list/i });
341263

342264
const sagaUpdatesElements = within(sagaChangesContainer).queryAllByRole("row");
343265
//from within each sagaUpdatesElements get the values of an element with aria-label="time stamp"
344-
//and check if the values are in the same order as the sagaUpdates array passed to this function
266+
//and check if the values are in the same order as the expectedDatesInOrder array passed to this function
345267
const sagaUpdatesTimestamps = sagaUpdatesElements.map((item: HTMLElement) => within(item).getByLabelText("time stamp"));
346268

347-
//expect the number of found sagaUpdatesTimestamps to be the same as the number of sagaUpdates passed to this function
348-
expect(sagaUpdatesTimestamps).toHaveLength(sagaUpdates.length);
269+
//expect the number of found sagaUpdatesTimestamps to be the same as the number of expected dates passed to this function
270+
expect(sagaUpdatesTimestamps).toHaveLength(expectedDatesInOrder.length);
349271

350272
const sagaUpdatesTimestampsValues = sagaUpdatesTimestamps.map((item) => item.innerHTML);
351273

352-
// Parse the rendered timestamp strings back to Date objects for comparison
353-
const parsedDatesFromUI = sagaUpdatesTimestampsValues.map((timestampString) => {
354-
// Parse the retrieved timestamp string back to a Date
355-
return new Date(timestampString);
356-
});
357-
358-
const expectedDates = sagaUpdates.map((item) => item.expectedTime);
274+
// Verify we have the same number of rendered timestamps as expected dates
275+
expect(sagaUpdatesTimestampsValues).toHaveLength(expectedDatesInOrder.length);
359276

360-
// Compare the dates directly
361-
expect(parsedDatesFromUI).toEqual(expectedDates);
277+
// For each rendered timestamp, verify it matches the expected date at that position
278+
// by formatting the expected date the same way and comparing strings
279+
expectedDatesInOrder.forEach((expectedDate, index) => {
280+
const expectedFormattedString = expectedDate.toLocaleString();
281+
expect(sagaUpdatesTimestampsValues[index]).toBe(expectedFormattedString);
282+
});
362283
},
363284
},
364285
};

src/Frontend/src/components/messages/SagaDiagram.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import SagaPluginNeeded from "./SagaDiagram/SagaPluginNeeded.vue";
1313
import SagaHeader from "./SagaDiagram/SagaHeader.vue";
1414
import SagaUpdateNode from "./SagaDiagram/SagaUpdateNode.vue";
1515
import SagaCompletedNode from "./SagaDiagram/SagaCompletedNode.vue";
16-
import { toLocalDateTimeString } from "@/composables/formatUtils";
1716
1817
const sagaDiagramStore = useSagaDiagramStore();
1918
const { showMessageData, loading } = storeToRefs(sagaDiagramStore);
@@ -57,7 +56,7 @@ const vm = computed<SagaViewModel>(() => {
5756
SagaCompleted: !!completedUpdate,
5857
5958
// Display data
60-
FormattedCompletionTime: completionTime ? toLocalDateTimeString(completionTime) : "",
59+
FormattedCompletionTime: completionTime ? completionTime.toLocaleString() : "",
6160
SagaUpdates: parseSagaUpdates(sagaHistory, sagaDiagramStore.messagesData),
6261
ShowMessageData: showMessageData.value,
6362
};

src/Frontend/src/components/messages/SagaDiagram/SagaDiagramParser.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export function parseSagaUpdates(sagaHistory: SagaHistory | null, messagesData:
9393
return {
9494
MessageType: msg.message_type || "",
9595
MessageId: msg.message_id,
96-
FormattedTimeSent: `${timeSent.toLocaleDateString()} ${timeSent.toLocaleTimeString()}`,
96+
FormattedTimeSent: timeSent.toLocaleString(),
9797
HasTimeout: hasTimeout,
9898
TimeoutSeconds: timeoutSeconds,
9999
TimeoutFriendly: getTimeoutFriendly(delivery_delay),
@@ -128,13 +128,13 @@ export function parseSagaUpdates(sagaHistory: SagaHistory | null, messagesData:
128128
MessageId: update.initiating_message?.message_id || "",
129129
StartTime: startTime,
130130
FinishTime: finishTime,
131-
FormattedStartTime: `${startTime.toLocaleDateString()} ${startTime.toLocaleTimeString()}`,
131+
FormattedStartTime: startTime.toLocaleString(),
132132
Status: update.status,
133133
StatusDisplay: update.status === "new" ? "Saga Initiated" : "Saga Updated",
134134
InitiatingMessage: <InitiatingMessageViewModel>{
135135
FriendlyTypeName: typeToName(update.initiating_message?.message_type || "Unknown Message") || "",
136136
MessageId: update.initiating_message?.message_id || "",
137-
FormattedMessageTimestamp: `${initiatingMessageTimestamp.toLocaleDateString()} ${initiatingMessageTimestamp.toLocaleTimeString()}`,
137+
FormattedMessageTimestamp: initiatingMessageTimestamp.toLocaleString(),
138138
MessageData: initiatingMessageData,
139139
IsEventMessage: update.initiating_message?.intent === "Publish",
140140
IsSagaTimeoutMessage: update.initiating_message?.is_saga_timeout_message || false,

src/Frontend/src/composables/formatUtils.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,3 @@ export function dotNetTimespanToMilliseconds(timespan: string) {
1717
const [hh, mm, ss] = timespan.split(":");
1818
return ((parseInt(hh) * 60 + parseInt(mm)) * 60 + parseFloat(ss)) * 1000;
1919
}
20-
21-
export function toLocalDateTimeString(date: Date) {
22-
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
23-
}

0 commit comments

Comments
 (0)