mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-09-06 22:56:49 +08:00
feat(account page): change test activity graph starting day depending on the browser locale (@fehmer) (#6385)
implements #6356
This commit is contained in:
parent
821478e617
commit
978878c180
9 changed files with 626 additions and 75 deletions
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
TestActivityCalendar,
|
||||
ModifiableTestActivityCalendar,
|
||||
TestActivityDay,
|
||||
} from "../../src/ts/elements/test-activity-calendar";
|
||||
import * as Dates from "date-fns";
|
||||
import { MatcherResult } from "../vitest";
|
||||
|
@ -18,7 +19,7 @@ describe("test-activity-calendar.ts", () => {
|
|||
it("for lastDay in april", () => {
|
||||
//set today
|
||||
vi.setSystemTime(getDate("2024-04-10"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-04-10"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-04-10"), 0);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
|
@ -71,11 +72,67 @@ describe("test-activity-calendar.ts", () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
it("for lastDay in april start weeks on monday", () => {
|
||||
//set today
|
||||
vi.setSystemTime(getDate("2024-04-10"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-04-10"), 1);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
text: "apr",
|
||||
weeks: 3,
|
||||
},
|
||||
{
|
||||
text: "may",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "jun",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "jul",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "aug",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "sep",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "oct",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "nov",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "dec",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "jan",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "feb",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "mar",
|
||||
weeks: 4,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("for lastDay in april, not test for the current week", () => {
|
||||
//set today
|
||||
vi.setSystemTime(getDate("2024-04-24"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-04-10"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-04-10"), 0);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
|
@ -132,10 +189,71 @@ describe("test-activity-calendar.ts", () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("for lastDay in april, not test for the current week start weeks on monday", () => {
|
||||
//set today
|
||||
vi.setSystemTime(getDate("2024-04-24"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-04-10"), 1);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
text: "",
|
||||
weeks: 1,
|
||||
},
|
||||
{
|
||||
text: "may",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "jun",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "jul",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "aug",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "sep",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "oct",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "nov",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "dec",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "jan",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "feb",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "mar",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "apr",
|
||||
weeks: 4,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it("for lastDay in january", () => {
|
||||
//set today
|
||||
vi.setSystemTime(getDate("2023-01-01"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2023-01-01"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2023-01-01"), 0);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
|
@ -192,6 +310,7 @@ describe("test-activity-calendar.ts", () => {
|
|||
const calendar = new TestActivityCalendar(
|
||||
[],
|
||||
getDate("2023-05-10"),
|
||||
0,
|
||||
true
|
||||
);
|
||||
|
||||
|
@ -246,10 +365,70 @@ describe("test-activity-calendar.ts", () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
it("for lastDay and full year starting with sunday start weeks on monday", () => {
|
||||
const calendar = new TestActivityCalendar(
|
||||
[],
|
||||
getDate("2023-05-10"),
|
||||
1,
|
||||
true
|
||||
);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
text: "jan",
|
||||
weeks: 6,
|
||||
},
|
||||
{
|
||||
text: "feb",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "mar",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "apr",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "may",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "jun",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "jul",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "aug",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "sep",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "oct",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "nov",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "dec",
|
||||
weeks: 4,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it("for lastDay and full year starting with monday", () => {
|
||||
const calendar = new TestActivityCalendar(
|
||||
[],
|
||||
getDate("2024-05-10"),
|
||||
0,
|
||||
true
|
||||
);
|
||||
|
||||
|
@ -304,10 +483,69 @@ describe("test-activity-calendar.ts", () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
it("for lastDay and full year starting with monday start weeks on Monday", () => {
|
||||
const calendar = new TestActivityCalendar(
|
||||
[],
|
||||
getDate("2024-05-10"),
|
||||
1,
|
||||
true
|
||||
);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
text: "jan",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "feb",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "mar",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "apr",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "may",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "jun",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "jul",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "aug",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "sep",
|
||||
weeks: 5,
|
||||
},
|
||||
{
|
||||
text: "oct",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "nov",
|
||||
weeks: 4,
|
||||
},
|
||||
{
|
||||
text: "dec",
|
||||
weeks: 5,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it("for first day in june", () => {
|
||||
//set today
|
||||
vi.setSystemTime(getDate("2024-06-01"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-06-01"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-06-01"), 0);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
|
@ -363,7 +601,7 @@ describe("test-activity-calendar.ts", () => {
|
|||
it("no double month for for 16th june", () => {
|
||||
//set today
|
||||
vi.setSystemTime(getDate("2024-06-16"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-06-01"));
|
||||
const calendar = new TestActivityCalendar([], getDate("2024-06-01"), 0);
|
||||
|
||||
expect(calendar.getMonths()).toEqual([
|
||||
{
|
||||
|
@ -426,7 +664,11 @@ describe("test-activity-calendar.ts", () => {
|
|||
it("for lastDay in april", () => {
|
||||
const data = getData("2023-04-10", "2024-04-10");
|
||||
vi.setSystemTime(getDate("2024-04-30"));
|
||||
const calendar = new TestActivityCalendar(data, getDate("2024-04-10"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2024-04-10"),
|
||||
0
|
||||
);
|
||||
const days = calendar.getDays();
|
||||
|
||||
expect(days).toHaveLength(1 + 366 + 4); //one filler on the start, 366 days in leap year, four fillers at the end
|
||||
|
@ -454,13 +696,59 @@ describe("test-activity-calendar.ts", () => {
|
|||
for (let day = 347; day <= 366; day++) {
|
||||
expect(days[day]).toHaveLevel(0);
|
||||
}
|
||||
for (let day = 367; day <= 370; day++) {
|
||||
expect(days[day]).toBeFiller();
|
||||
}
|
||||
});
|
||||
it("for lastDay in april start weeks on Monday", () => {
|
||||
const data = getData("2023-04-10", "2024-04-10");
|
||||
vi.setSystemTime(getDate("2024-04-30"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2024-04-10"),
|
||||
1
|
||||
);
|
||||
const days = calendar.getDays();
|
||||
|
||||
expect(days).toHaveLength(1 + 366 + 4); //one filler on the start, 366 days in leap year, four fillers at the end
|
||||
|
||||
//may 23 starts with a monday
|
||||
expect(days[0]).toBeDate("2023-05-01").toHaveTests(121);
|
||||
|
||||
expect(days[1]).toBeDate("2023-05-02").toHaveTests(122).toHaveLevel(2);
|
||||
|
||||
expect(days[244])
|
||||
.toBeDate("2023-12-31")
|
||||
.toHaveTests(365)
|
||||
.toHaveLevel(4);
|
||||
|
||||
expect(days[245]).toBeDate("2024-01-01").toHaveTests(1).toHaveLevel(1);
|
||||
|
||||
expect(days[304]).toBeDate("2024-02-29").toHaveTests(60).toHaveLevel(1);
|
||||
|
||||
expect(days[345])
|
||||
.toBeDate("2024-04-10")
|
||||
.toHaveTests(101)
|
||||
.toHaveLevel(2);
|
||||
|
||||
//days from April 11th to April 30th
|
||||
for (let day = 346; day <= 365; day++) {
|
||||
expect(days[day]).toHaveLevel(0);
|
||||
}
|
||||
for (let day = 366; day <= 370; day++) {
|
||||
expect(days[day]).toBeFiller();
|
||||
}
|
||||
});
|
||||
|
||||
it("for full leap year", () => {
|
||||
//GIVEN
|
||||
const data = getData("2024-01-01", "2024-12-31");
|
||||
vi.setSystemTime(getDate("2024-12-31"));
|
||||
const calendar = new TestActivityCalendar(data, getDate("2024-12-31"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2024-12-31"),
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
const days = calendar.getDays();
|
||||
|
@ -484,13 +772,46 @@ describe("test-activity-calendar.ts", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("for full leap year start weeks on Monday", () => {
|
||||
//GIVEN
|
||||
const data = getData("2024-01-01", "2024-12-31");
|
||||
vi.setSystemTime(getDate("2024-12-31"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2024-12-31"),
|
||||
1
|
||||
);
|
||||
|
||||
//WHEN
|
||||
const days = calendar.getDays();
|
||||
|
||||
//THEN
|
||||
expect(days).toHaveLength(1 + 366 + 4); //one filler on the start, 366 days in leap year, four fillers at the end
|
||||
|
||||
//2024 starts with a monday
|
||||
expect(days[0]).toBeDate("2024-01-01");
|
||||
|
||||
expect(days[1]).toBeDate("2024-01-02").toHaveTests(2).toHaveLevel(1);
|
||||
expect(days[59]).toBeDate("2024-02-29").toHaveTests(60).toHaveLevel(1);
|
||||
expect(days[365])
|
||||
.toBeDate("2024-12-31")
|
||||
.toHaveTests(366)
|
||||
.toHaveLevel(4);
|
||||
|
||||
//2024 ends with a thuesday
|
||||
for (let day = 366; day < 1 + 366 + 4; day++) {
|
||||
expect(days[day]).toBeFiller();
|
||||
}
|
||||
});
|
||||
|
||||
it("for full year", () => {
|
||||
//GIVEN
|
||||
const data = getData("2022-11-30", "2023-12-31");
|
||||
vi.setSystemTime(getDate("2023-12-31"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
new Date("2023-12-31T23:59:59Z")
|
||||
new Date("2023-12-31T23:59:59Z"),
|
||||
0
|
||||
); //2023-12-31T23:59:59Z
|
||||
|
||||
//WHEN
|
||||
|
@ -524,7 +845,11 @@ describe("test-activity-calendar.ts", () => {
|
|||
//GIVEN
|
||||
const data = getData("2023-03-28", "2024-04-10"); //extra data in front of the calendar
|
||||
vi.setSystemTime(getDate("2024-04-30"));
|
||||
const calendar = new TestActivityCalendar(data, getDate("2024-04-10"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2024-04-10"),
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
const days = calendar.getDays();
|
||||
|
@ -546,7 +871,11 @@ describe("test-activity-calendar.ts", () => {
|
|||
//GIVEN
|
||||
const data = getData("2024-04-01", "2024-04-10");
|
||||
vi.setSystemTime(getDate("2024-04-30"));
|
||||
const calendar = new TestActivityCalendar(data, getDate("2024-04-10"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2024-04-10"),
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
const days = calendar.getDays();
|
||||
|
@ -574,7 +903,11 @@ describe("test-activity-calendar.ts", () => {
|
|||
//GIVEN
|
||||
const data = getData("2022-02-10", "2023-02-10");
|
||||
vi.setSystemTime(getDate("2023-02-28"));
|
||||
const calendar = new TestActivityCalendar(data, getDate("2023-02-10"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2023-02-10"),
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
const days = calendar.getDays();
|
||||
|
@ -608,7 +941,11 @@ describe("test-activity-calendar.ts", () => {
|
|||
//GIVEN
|
||||
const data = getData("2022-02-10", "2023-02-10");
|
||||
vi.setSystemTime(getDate("2023-02-12"));
|
||||
const calendar = new TestActivityCalendar(data, getDate("2023-02-10"));
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2023-02-10"),
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
const days = calendar.getDays();
|
||||
|
@ -639,6 +976,7 @@ describe("test-activity-calendar.ts", () => {
|
|||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2024-02-10"),
|
||||
0,
|
||||
true
|
||||
);
|
||||
|
||||
|
@ -657,10 +995,38 @@ describe("test-activity-calendar.ts", () => {
|
|||
expect(days[day]).toHaveLevel(0);
|
||||
}
|
||||
//december 24 ends with a tuesday
|
||||
expect(days[367]).toBeFiller();
|
||||
expect(days[368]).toBeFiller();
|
||||
expect(days[369]).toBeFiller();
|
||||
expect(days[370]).toBeFiller();
|
||||
for (let day = 367; day <= 370; day++) {
|
||||
expect(days[day]).toBeFiller();
|
||||
}
|
||||
});
|
||||
it("for lastDay in february full year start weeks on Monday", () => {
|
||||
//GIVEN
|
||||
const data = getData("2023-02-10", "2024-02-10");
|
||||
const calendar = new TestActivityCalendar(
|
||||
data,
|
||||
getDate("2024-02-10"),
|
||||
1,
|
||||
true
|
||||
);
|
||||
|
||||
//WHEN
|
||||
const days = calendar.getDays();
|
||||
|
||||
//THEN
|
||||
//january 24 starts with a monday,
|
||||
expect(days[0]).toBeDate("2024-01-01").toHaveTests(1).toHaveLevel(1);
|
||||
|
||||
expect(days[1]).toBeDate("2024-01-02").toHaveTests(2).toHaveLevel(1);
|
||||
expect(days[40]).toBeDate("2024-02-10").toHaveTests(41).toHaveLevel(4);
|
||||
|
||||
//days from 11th february to 31th december
|
||||
for (let day = 41; day <= 365; day++) {
|
||||
expect(days[day]).toHaveLevel(0);
|
||||
}
|
||||
//december 24 ends with a tuesday
|
||||
for (let day = 366; day <= 370; day++) {
|
||||
expect(days[day]).toBeFiller();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -672,7 +1038,8 @@ describe("test-activity-calendar.ts", () => {
|
|||
vi.setSystemTime(getDate("2024-04-30"));
|
||||
const calendar = new ModifiableTestActivityCalendar(
|
||||
[1, 2, 3],
|
||||
lastDate
|
||||
lastDate,
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
|
@ -693,7 +1060,8 @@ describe("test-activity-calendar.ts", () => {
|
|||
vi.setSystemTime(getDate("2024-04-10"));
|
||||
const calendar = new ModifiableTestActivityCalendar(
|
||||
[1, 2, 3],
|
||||
lastDate
|
||||
lastDate,
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
|
@ -730,7 +1098,8 @@ describe("test-activity-calendar.ts", () => {
|
|||
vi.setSystemTime(getDate("2024-04-10"));
|
||||
const calendar = new ModifiableTestActivityCalendar(
|
||||
[1, 2, 3],
|
||||
getDate("2024-04-10")
|
||||
getDate("2024-04-10"),
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
|
@ -754,7 +1123,8 @@ describe("test-activity-calendar.ts", () => {
|
|||
vi.setSystemTime(getDate("2024-12-24"));
|
||||
const calendar = new ModifiableTestActivityCalendar(
|
||||
getData("2023-12-20", "2024-12-24"),
|
||||
getDate("2024-12-24")
|
||||
getDate("2024-12-24"),
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
|
@ -774,7 +1144,8 @@ describe("test-activity-calendar.ts", () => {
|
|||
//GIVEN
|
||||
const calendar = new ModifiableTestActivityCalendar(
|
||||
[1, 2, 3],
|
||||
getDate("2024-04-10")
|
||||
getDate("2024-04-10"),
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
|
@ -793,7 +1164,8 @@ describe("test-activity-calendar.ts", () => {
|
|||
const lastDate = getDate("2024-01-02");
|
||||
const calendar = new ModifiableTestActivityCalendar(
|
||||
[1, 2, 3, 4],
|
||||
lastDate
|
||||
lastDate,
|
||||
0
|
||||
);
|
||||
|
||||
//WHEN
|
||||
|
@ -825,7 +1197,8 @@ describe("test-activity-calendar.ts", () => {
|
|||
const lastDate = getDate("2024-01-02");
|
||||
const calendar = new ModifiableTestActivityCalendar(
|
||||
[1, 2, 3, 4],
|
||||
lastDate
|
||||
lastDate,
|
||||
0
|
||||
);
|
||||
|
||||
//THEN
|
||||
|
@ -852,10 +1225,7 @@ function getData(from: string, to: string): number[] {
|
|||
}
|
||||
|
||||
expect.extend({
|
||||
toBeDate(
|
||||
received: MonkeyTypes.TestActivityDay,
|
||||
expected: string
|
||||
): MatcherResult {
|
||||
toBeDate(received: TestActivityDay, expected: string): MatcherResult {
|
||||
const expectedDate = Dates.format(getDate(expected), "EEEE dd MMM yyyy");
|
||||
const actual = received.label?.substring(received.label.indexOf("on") + 3);
|
||||
|
||||
|
@ -866,10 +1236,7 @@ expect.extend({
|
|||
expected: expectedDate,
|
||||
};
|
||||
},
|
||||
toHaveTests(
|
||||
received: MonkeyTypes.TestActivityDay,
|
||||
expected: number
|
||||
): MatcherResult {
|
||||
toHaveTests(received: TestActivityDay, expected: number): MatcherResult {
|
||||
const expectedLabel = `${expected} ${expected == 1 ? "test" : "tests"}`;
|
||||
const actual = received.label?.substring(0, received.label.indexOf(" on"));
|
||||
|
||||
|
@ -881,7 +1248,7 @@ expect.extend({
|
|||
};
|
||||
},
|
||||
toHaveLevel(
|
||||
received: MonkeyTypes.TestActivityDay,
|
||||
received: TestActivityDay,
|
||||
expected: string | number
|
||||
): MatcherResult {
|
||||
return {
|
||||
|
@ -892,7 +1259,7 @@ expect.extend({
|
|||
};
|
||||
},
|
||||
|
||||
toBeFiller(received: MonkeyTypes.TestActivityDay): MatcherResult {
|
||||
toBeFiller(received: TestActivityDay): MatcherResult {
|
||||
return {
|
||||
pass: received.level === "filler",
|
||||
message: () => `Is not a filler.`,
|
||||
|
|
87
frontend/__tests__/utils/date-and-time.spec.ts
Normal file
87
frontend/__tests__/utils/date-and-time.spec.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
import * as DateAndTime from "../../src/ts/utils/date-and-time";
|
||||
|
||||
describe("date-and-time", () => {
|
||||
const testCases = [
|
||||
{ locale: "en-US", firstDayOfWeek: 0 },
|
||||
{ locale: "en", firstDayOfWeek: 0 },
|
||||
{ locale: "de-DE", firstDayOfWeek: 1 },
|
||||
{ locale: "en-DE", firstDayOfWeek: 1, firefoxFirstDayOfWeek: 0 },
|
||||
{ locale: "de-AT", firstDayOfWeek: 1 },
|
||||
{ locale: "ps-AF", firstDayOfWeek: 6, firefoxFirstDayOfWeek: 0 },
|
||||
{ locale: "de-unknown", firstDayOfWeek: 1 },
|
||||
{ locale: "xx-yy", firstDayOfWeek: 1, firefoxFirstDayOfWeek: 0 },
|
||||
];
|
||||
|
||||
describe("getFirstDayOfTheWeek", () => {
|
||||
const languageMock = vi.spyOn(window.navigator, "language", "get");
|
||||
const localeMock = vi.spyOn(Intl, "Locale");
|
||||
|
||||
beforeEach(() => {
|
||||
languageMock.mockReset();
|
||||
localeMock.mockReset();
|
||||
});
|
||||
|
||||
it("fallback to sunday for missing language", () => {
|
||||
//GIVEN
|
||||
languageMock.mockReturnValue(null as any);
|
||||
|
||||
//WHEN / THEN
|
||||
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(0);
|
||||
});
|
||||
|
||||
describe("with weekInfo", () => {
|
||||
it.for(testCases)(`$locale`, ({ locale, firstDayOfWeek }) => {
|
||||
//GIVEN
|
||||
languageMock.mockReturnValue(locale);
|
||||
localeMock.mockImplementationOnce(
|
||||
() => ({ weekInfo: { firstDay: firstDayOfWeek } } as any)
|
||||
);
|
||||
|
||||
//WHEN/THEN
|
||||
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(firstDayOfWeek);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with getWeekInfo", () => {
|
||||
it("with getWeekInfo on monday", () => {
|
||||
languageMock.mockReturnValue("en-US");
|
||||
localeMock.mockImplementationOnce(
|
||||
() => ({ getWeekInfo: () => ({ firstDay: 1 }) } as any)
|
||||
);
|
||||
|
||||
//WHEN/THEN
|
||||
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(1);
|
||||
});
|
||||
it("with getWeekInfo on sunday", () => {
|
||||
languageMock.mockReturnValue("en-US");
|
||||
localeMock.mockImplementationOnce(
|
||||
() => ({ getWeekInfo: () => ({ firstDay: 7 }) } as any)
|
||||
);
|
||||
|
||||
//WHEN/THEN
|
||||
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("without weekInfo (firefox)", () => {
|
||||
beforeEach(() => {
|
||||
localeMock.mockImplementationOnce(() => ({} as any));
|
||||
});
|
||||
|
||||
it.for(testCases)(
|
||||
`$locale`,
|
||||
({ locale, firstDayOfWeek, firefoxFirstDayOfWeek }) => {
|
||||
//GIVEN
|
||||
languageMock.mockReturnValue(locale);
|
||||
|
||||
//WHEN/THEN
|
||||
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(
|
||||
firefoxFirstDayOfWeek !== undefined
|
||||
? firefoxFirstDayOfWeek
|
||||
: firstDayOfWeek
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
3
frontend/__tests__/vitest.d.ts
vendored
3
frontend/__tests__/vitest.d.ts
vendored
|
@ -1,6 +1,7 @@
|
|||
import type { Assertion, AsymmetricMatchersContaining } from "vitest";
|
||||
import { TestActivityDay } from "../src/ts/elements/test-activity-calendar";
|
||||
|
||||
interface ActivityDayMatchers<R = MonkeyTypes.TestActivityDay> {
|
||||
interface ActivityDayMatchers<R = TestActivityDay> {
|
||||
toBeDate: (date: string) => ActivityDayMatchers<R>;
|
||||
toHaveTests: (tests: number) => ActivityDayMatchers<R>;
|
||||
toHaveLevel: (level?: string | number) => ActivityDayMatchers<R>;
|
||||
|
|
|
@ -309,24 +309,8 @@
|
|||
</div>
|
||||
<div class="activity"></div>
|
||||
<div class="months"></div>
|
||||
<div class="daysFull">
|
||||
<div></div>
|
||||
<div><div class="text">monday</div></div>
|
||||
<div></div>
|
||||
<div><div class="text">wednesday</div></div>
|
||||
<div></div>
|
||||
<div><div class="text">friday</div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="days">
|
||||
<div></div>
|
||||
<div><div class="text">mon</div></div>
|
||||
<div></div>
|
||||
<div><div class="text">wed</div></div>
|
||||
<div></div>
|
||||
<div><div class="text">fri</div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="daysFull"></div>
|
||||
<div class="days"></div>
|
||||
<div class="nodata hidden">No data found.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -522,10 +522,6 @@
|
|||
font-size: var(--font-size);
|
||||
}
|
||||
|
||||
.days {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.daysFull {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
@ -551,6 +547,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.days {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nodata {
|
||||
grid-area: chart;
|
||||
}
|
||||
|
|
|
@ -29,8 +29,10 @@ import {
|
|||
} from "./constants/default-snapshot";
|
||||
import { getDefaultConfig } from "./constants/default-config";
|
||||
import { FunboxMetadata } from "../../../packages/funbox/src/types";
|
||||
import { getFirstDayOfTheWeek } from "./utils/date-and-time";
|
||||
|
||||
let dbSnapshot: Snapshot | undefined;
|
||||
const firstDayOfTheWeek = getFirstDayOfTheWeek();
|
||||
|
||||
export class SnapshotInitError extends Error {
|
||||
constructor(message: string, public responseCode: number) {
|
||||
|
@ -163,7 +165,8 @@ export async function initSnapshot(): Promise<Snapshot | number | boolean> {
|
|||
if (userData.testActivity !== undefined) {
|
||||
snap.testActivity = new ModifiableTestActivityCalendar(
|
||||
userData.testActivity.testsByDays,
|
||||
new Date(userData.testActivity.lastDay)
|
||||
new Date(userData.testActivity.lastDay),
|
||||
firstDayOfTheWeek
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1055,6 +1058,7 @@ export async function getTestActivityCalendar(
|
|||
dbSnapshot.testActivityByYear[year] = new TestActivityCalendar(
|
||||
testsByDays,
|
||||
lastDay,
|
||||
firstDayOfTheWeek,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,16 +11,15 @@ import {
|
|||
startOfYear,
|
||||
differenceInWeeks,
|
||||
startOfMonth,
|
||||
nextSunday,
|
||||
previousSunday,
|
||||
isSunday,
|
||||
nextSaturday,
|
||||
isSaturday,
|
||||
subWeeks,
|
||||
Interval,
|
||||
toDate,
|
||||
previousDay,
|
||||
Day,
|
||||
nextDay,
|
||||
} from "date-fns";
|
||||
|
||||
type TestActivityDay = {
|
||||
export type TestActivityDay = {
|
||||
level: string;
|
||||
label?: string;
|
||||
};
|
||||
|
@ -35,12 +34,15 @@ export class TestActivityCalendar implements TestActivityCalendar {
|
|||
protected startDay: Date;
|
||||
protected endDay: Date;
|
||||
protected isFullYear: boolean;
|
||||
public firstDayOfWeek: Day;
|
||||
|
||||
constructor(
|
||||
data: (number | null | undefined)[],
|
||||
lastDay: Date,
|
||||
firstDayOfWeek: Day,
|
||||
fullYear = false
|
||||
) {
|
||||
this.firstDayOfWeek = firstDayOfWeek;
|
||||
const local = new UTCDateMini(lastDay);
|
||||
const interval = this.getInterval(local, fullYear);
|
||||
|
||||
|
@ -56,7 +58,9 @@ export class TestActivityCalendar implements TestActivityCalendar {
|
|||
if (!fullYear) {
|
||||
//show the last 52 weeks. Not using one year to avoid the graph to show 54 weeks
|
||||
start = addDays(subWeeks(end, 52), 1);
|
||||
if (!isSunday(start)) start = previousSunday(start);
|
||||
if (!this.isFirstDayOfWeek(start)) {
|
||||
start = this.previousFirstDayOfWeek(start);
|
||||
}
|
||||
}
|
||||
|
||||
return { start, end };
|
||||
|
@ -91,11 +95,14 @@ export class TestActivityCalendar implements TestActivityCalendar {
|
|||
let start = i === 0 ? this.startDay : startOfMonth(month);
|
||||
let end = i === months.length - 1 ? this.endDay : endOfMonth(start);
|
||||
|
||||
if (!isSunday(start)) {
|
||||
start = (i === 0 ? previousSunday : nextSunday)(start);
|
||||
if (!this.isFirstDayOfWeek(start)) {
|
||||
start =
|
||||
i === 0
|
||||
? this.previousFirstDayOfWeek(start)
|
||||
: this.nextFirstDayOfWeek(start);
|
||||
}
|
||||
if (!isSaturday(end)) {
|
||||
end = nextSaturday(end);
|
||||
if (!this.isLastDayOfWeek(end)) {
|
||||
end = this.nextLastDayOfWeek(end);
|
||||
}
|
||||
|
||||
const weeks = differenceInWeeks(end, start, { roundingMethod: "ceil" });
|
||||
|
@ -124,7 +131,7 @@ export class TestActivityCalendar implements TestActivityCalendar {
|
|||
};
|
||||
|
||||
//skip weekdays in the previous month
|
||||
for (let i = 0; i < this.startDay.getDay(); i++) {
|
||||
for (let i = 0; i < this.startDay.getDay() - this.firstDayOfWeek; i++) {
|
||||
result.push({
|
||||
level: "filler",
|
||||
});
|
||||
|
@ -148,7 +155,7 @@ export class TestActivityCalendar implements TestActivityCalendar {
|
|||
}
|
||||
|
||||
//add weekdays missing
|
||||
for (let i = this.endDay.getDay(); i < 6; i++) {
|
||||
for (let i = this.endDay.getDay() - this.firstDayOfWeek; i < 6; i++) {
|
||||
result.push({
|
||||
level: "filler",
|
||||
});
|
||||
|
@ -178,6 +185,22 @@ export class TestActivityCalendar implements TestActivityCalendar {
|
|||
const mid = sum / trimmed.length;
|
||||
return [Math.floor(mid / 2), Math.round(mid), Math.round(mid * 1.5)];
|
||||
}
|
||||
|
||||
private isFirstDayOfWeek(date: Date): boolean {
|
||||
return toDate(date).getDay() === this.firstDayOfWeek;
|
||||
}
|
||||
private previousFirstDayOfWeek(date: Date): Date {
|
||||
return previousDay(date, this.firstDayOfWeek);
|
||||
}
|
||||
private nextFirstDayOfWeek(date: Date): Date {
|
||||
return nextDay(date, this.firstDayOfWeek);
|
||||
}
|
||||
private isLastDayOfWeek(date: Date): boolean {
|
||||
return toDate(date).getDay() === (this.firstDayOfWeek + 6) % 7;
|
||||
}
|
||||
private nextLastDayOfWeek(date: Date): Date {
|
||||
return nextDay(date, ((this.firstDayOfWeek + 6) % 7) as Day);
|
||||
}
|
||||
}
|
||||
|
||||
export class ModifiableTestActivityCalendar
|
||||
|
@ -186,8 +209,8 @@ export class ModifiableTestActivityCalendar
|
|||
{
|
||||
private lastDay: Date;
|
||||
|
||||
constructor(data: (number | null)[], lastDay: Date) {
|
||||
super(data, lastDay);
|
||||
constructor(data: (number | null)[], lastDay: Date, firstDayOfWeek: Day) {
|
||||
super(data, lastDay, firstDayOfWeek);
|
||||
this.lastDay = new UTCDateMini(lastDay);
|
||||
}
|
||||
|
||||
|
@ -218,9 +241,14 @@ export class ModifiableTestActivityCalendar
|
|||
getFullYearCalendar(): TestActivityCalendar {
|
||||
const today = new Date();
|
||||
if (this.lastDay.getFullYear() !== new UTCDateMini(today).getFullYear()) {
|
||||
return new TestActivityCalendar([], today, true);
|
||||
return new TestActivityCalendar([], today, this.firstDayOfWeek, true);
|
||||
} else {
|
||||
return new TestActivityCalendar(this.data, this.lastDay, true);
|
||||
return new TestActivityCalendar(
|
||||
this.data,
|
||||
this.lastDay,
|
||||
this.firstDayOfWeek,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ export function init(
|
|||
|
||||
yearSelector = getYearSelector();
|
||||
initYearSelector("current", userSignUpDate?.getFullYear() || 2022);
|
||||
updateLabels(calendar.firstDayOfWeek);
|
||||
update(calendar);
|
||||
}
|
||||
|
||||
|
@ -128,3 +129,40 @@ function getYearSelector(): SlimSelect {
|
|||
});
|
||||
return yearSelector;
|
||||
}
|
||||
|
||||
const daysDisplay = [
|
||||
"sunday",
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
];
|
||||
function updateLabels(firstDayOfWeek: number): void {
|
||||
const days: (string | undefined)[] = [];
|
||||
for (let i = 0; i < 7; i++) {
|
||||
days.push(
|
||||
i % 2 != firstDayOfWeek % 2
|
||||
? daysDisplay[(firstDayOfWeek + i) % 7]
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
const buildHtml = (maxLength?: number): string => {
|
||||
const shorten =
|
||||
maxLength !== undefined
|
||||
? (it: string) => it.substring(0, maxLength)
|
||||
: (it: string) => it;
|
||||
return days
|
||||
.map((it) =>
|
||||
it !== undefined
|
||||
? `<div><div class="text">${shorten(it)}</div></div>`
|
||||
: "<div></div>"
|
||||
)
|
||||
.join("");
|
||||
};
|
||||
|
||||
$("#testActivity .daysFull").html(buildHtml());
|
||||
$("#testActivity .days").html(buildHtml(3));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { roundTo2 } from "@monkeytype/util/numbers";
|
||||
|
||||
import { Day } from "date-fns";
|
||||
import * as Locales from "date-fns/locale";
|
||||
/**
|
||||
* Converts seconds to a human-readable string representation of time.
|
||||
* @param sec The number of seconds to convert.
|
||||
|
@ -104,3 +105,44 @@ export function secondsToString(
|
|||
}
|
||||
return ret.trim();
|
||||
}
|
||||
|
||||
export function getFirstDayOfTheWeek(): Day {
|
||||
if (navigator.language === undefined || navigator.language === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const locale = new Intl.Locale(navigator.language);
|
||||
if (locale === undefined || locale === null) {
|
||||
return 0; //sunday
|
||||
}
|
||||
|
||||
//modern browsers support `weekInfo` or `getWeekInfo()`
|
||||
if ("weekInfo" in locale) {
|
||||
// @ts-ignore
|
||||
return (locale.weekInfo.firstDay as number) % 7;
|
||||
}
|
||||
|
||||
if ("getWeekInfo" in locale) {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
return (locale.getWeekInfo().firstDay as number) % 7;
|
||||
}
|
||||
|
||||
//use date-fns for browsers like firefox
|
||||
// @ts-ignore
|
||||
let dateFnsLocale = Locales[
|
||||
navigator.language.replaceAll("-", "")
|
||||
] as Locales.Locale;
|
||||
|
||||
if (dateFnsLocale === undefined || dateFnsLocale === null) {
|
||||
//retry with language only
|
||||
// @ts-ignore
|
||||
dateFnsLocale = Locales[navigator.language.split("-")[0]] as Locales.Locale;
|
||||
}
|
||||
|
||||
if (dateFnsLocale !== undefined && dateFnsLocale !== null) {
|
||||
return ((dateFnsLocale.options?.weekStartsOn ?? 0) % 7) as Day;
|
||||
}
|
||||
|
||||
return 0; //start on sunday
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue