feat(stats): show work and time-off lengths (#66)
This commit is contained in:
1 parent
d9f3f8d34a
commit
3fbf9c3172
12 files changed
+373
-20
No files matched your search
@@ -48,6 +48,129 @@ async function runInNonBrowser(request: Parameters<typeof planLeave>[0]) {
|
||||
beforeEach(() => vi.setSystemTime(new Date('2024-01-01T12:00:00Z')));
|
||||
afterEach(() => vi.useRealTimers());
|
||||
|
||||
describe('calculated work-sequence and time-off-period statistics', () => {
|
||||
it('keeps fractional work means and merges consecutive declared days across a weekend', async () => {
|
||||
const result = isPlan(
|
||||
await runInNonBrowser({
|
||||
horizon: { start: '2024-01-02', end: '2024-01-11' },
|
||||
balances: [],
|
||||
rules: [],
|
||||
declaredDays: ['2024-01-05', '2024-01-08'].map((date) => ({
|
||||
date,
|
||||
type: 'other',
|
||||
})),
|
||||
}),
|
||||
);
|
||||
expect(result.metrics).toMatchObject({
|
||||
nonWorkingDayGain: 2,
|
||||
averageWorkSequence: 2.5,
|
||||
longestWorkSequence: 3,
|
||||
averageTimeOffPeriod: 4,
|
||||
longestTimeOffPeriod: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
scenario: 'averages the 10, 5 and 15 working days including both horizon edges',
|
||||
horizon: { start: '2024-01-08', end: '2024-02-21' },
|
||||
declaredDates: ['2024-01-22', '2024-01-30'],
|
||||
metrics: {
|
||||
averageWorkSequence: 10,
|
||||
longestWorkSequence: 15,
|
||||
averageTimeOffPeriod: 2,
|
||||
longestTimeOffPeriod: 3,
|
||||
nonWorkingDayGain: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
scenario: 'keeps the unrounded mean of work sequences of 3, 1 and 6 days',
|
||||
horizon: { start: '2024-01-02', end: '2024-01-18' },
|
||||
declaredDates: ['2024-01-05', '2024-01-09'],
|
||||
metrics: {
|
||||
averageWorkSequence: 10 / 3,
|
||||
longestWorkSequence: 6,
|
||||
averageTimeOffPeriod: 2,
|
||||
longestTimeOffPeriod: 3,
|
||||
nonWorkingDayGain: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
scenario: 'averages one-day and two-day breaks without adding zero-length work samples',
|
||||
horizon: { start: '2024-01-02', end: '2024-01-12' },
|
||||
declaredDates: ['2024-01-03', '2024-01-09', '2024-01-10'],
|
||||
metrics: {
|
||||
averageWorkSequence: 5 / 3,
|
||||
longestWorkSequence: 3,
|
||||
averageTimeOffPeriod: 1.5,
|
||||
longestTimeOffPeriod: 2,
|
||||
nonWorkingDayGain: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
scenario: 'does not count or split work on intervening holidays and weekends',
|
||||
horizon: { start: '2024-05-06', end: '2024-05-15' },
|
||||
declaredDates: [],
|
||||
metrics: {
|
||||
averageWorkSequence: 5,
|
||||
longestWorkSequence: 5,
|
||||
averageTimeOffPeriod: null,
|
||||
longestTimeOffPeriod: null,
|
||||
nonWorkingDayGain: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
scenario:
|
||||
'retains an empty work average and a measured zero when every workday is declared',
|
||||
horizon: { start: '2024-01-08', end: '2024-01-13' },
|
||||
declaredDates: ['2024-01-08', '2024-01-09', '2024-01-10', '2024-01-11', '2024-01-12'],
|
||||
metrics: {
|
||||
averageWorkSequence: null,
|
||||
longestWorkSequence: 0,
|
||||
averageTimeOffPeriod: 9,
|
||||
longestTimeOffPeriod: 9,
|
||||
nonWorkingDayGain: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
scenario: 'distinguishes no samples from zero gain in a weekend-only horizon',
|
||||
horizon: { start: '2024-01-06', end: '2024-01-08' },
|
||||
declaredDates: [],
|
||||
metrics: {
|
||||
averageWorkSequence: null,
|
||||
longestWorkSequence: 0,
|
||||
averageTimeOffPeriod: null,
|
||||
longestTimeOffPeriod: null,
|
||||
nonWorkingDayGain: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
scenario:
|
||||
'counts Christmas on a Saturday once alongside its Sunday in a declared break',
|
||||
horizon: { start: '2021-12-20', end: '2021-12-28' },
|
||||
declaredDates: ['2021-12-24'],
|
||||
metrics: {
|
||||
averageWorkSequence: 2.5,
|
||||
longestWorkSequence: 4,
|
||||
averageTimeOffPeriod: 3,
|
||||
longestTimeOffPeriod: 3,
|
||||
nonWorkingDayGain: 2,
|
||||
},
|
||||
},
|
||||
])('$scenario', async ({ horizon, declaredDates, metrics }) => {
|
||||
const result = isPlan(
|
||||
await runInNonBrowser({
|
||||
horizon,
|
||||
balances: [],
|
||||
rules: [],
|
||||
declaredDays: declaredDates.map((date) => ({ date, type: 'other' })),
|
||||
}),
|
||||
);
|
||||
expect(result.placements).toEqual([]);
|
||||
expect(result.metrics).toMatchObject(metrics);
|
||||
});
|
||||
});
|
||||
|
||||
describe('declared holiday patterns and captured entitlement date', () => {
|
||||
const producer = {
|
||||
id: 'producer',
|
||||
@@ -1214,7 +1337,13 @@ describe('planLeave', () => {
|
||||
nonWorkingDayGain: 2,
|
||||
},
|
||||
]);
|
||||
expect(result.metrics).toMatchObject({ nonWorkingDayGain: 2, longestWorkSequence: 3 });
|
||||
expect(result.metrics).toMatchObject({
|
||||
nonWorkingDayGain: 2,
|
||||
averageWorkSequence: 2,
|
||||
longestWorkSequence: 3,
|
||||
averageTimeOffPeriod: 3,
|
||||
longestTimeOffPeriod: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it('merges declarations, placements, weekends and national holidays without counting dates twice', async () => {
|
||||
@@ -1254,7 +1383,14 @@ describe('planLeave', () => {
|
||||
nonWorkingDayGain: 6,
|
||||
},
|
||||
]);
|
||||
expect(result.metrics).toMatchObject({ nonWorkingDayGain: 6, holidayWeekValue: 5 });
|
||||
expect(result.metrics).toMatchObject({
|
||||
nonWorkingDayGain: 6,
|
||||
holidayWeekValue: 5,
|
||||
averageWorkSequence: null,
|
||||
longestWorkSequence: 0,
|
||||
averageTimeOffPeriod: 10,
|
||||
longestTimeOffPeriod: 10,
|
||||
});
|
||||
});
|
||||
|
||||
it('uses declared days to split work sequences while weekends and holidays alone never split them', async () => {
|
||||
@@ -1319,6 +1455,13 @@ describe('planLeave', () => {
|
||||
declaredDates: ['2024-01-02', '2024-01-03', '2024-01-05'],
|
||||
nonWorkingDayGain: 5,
|
||||
});
|
||||
expect(result.metrics).toMatchObject({
|
||||
nonWorkingDayGain: 5,
|
||||
averageWorkSequence: null,
|
||||
longestWorkSequence: 0,
|
||||
averageTimeOffPeriod: 9,
|
||||
longestTimeOffPeriod: 9,
|
||||
});
|
||||
const unanchored = isPlan(
|
||||
await runInNonBrowser({
|
||||
horizon: selectedHorizon,
|
||||
@@ -1328,7 +1471,13 @@ describe('planLeave', () => {
|
||||
}),
|
||||
);
|
||||
expect(unanchored.timeOffPeriods).toEqual([]);
|
||||
expect(unanchored.metrics.nonWorkingDayGain).toBe(0);
|
||||
expect(unanchored.metrics).toMatchObject({
|
||||
nonWorkingDayGain: 0,
|
||||
averageWorkSequence: 2,
|
||||
longestWorkSequence: 2,
|
||||
averageTimeOffPeriod: null,
|
||||
longestTimeOffPeriod: null,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
||||
Reference in new issue
Block a user