Skip to content

Commit

Permalink
Merge pull request #32 from the-collab-lab/ym-al-add-unit-tests
Browse files Browse the repository at this point in the history
Add unit tests to important date calculating and converting functions
  • Loading branch information
DwightTheShark committed May 19, 2023
2 parents f603208 + 853023d commit 8a4fb92
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 34 deletions.
24 changes: 0 additions & 24 deletions src/api/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import {
transformToJSDate,
getDaysBetweenDates,
getNextPurchaseDate,
getItemDaysUntilNextPurchase,
getItemDaysSinceLastPurchase,
sortItems,
} from '../utils';
import { calculateEstimate } from '@the-collab-lab/shopping-list-utils';
/**
Expand Down Expand Up @@ -126,24 +123,3 @@ export async function deleteItem(listId, item) {
return { success: false, error: error.message };
}
}

export function comparePurchaseUrgency(data) {
const inActiveItems = sortItems(
data.filter((item) => {
return getItemDaysSinceLastPurchase(item) > 60;
}),
);

const activeItems = sortItems(
data.filter((item) => {
return (
item.dateLastPurchased === null ||
getItemDaysSinceLastPurchase(item) < 60
);
}),
);

const sortedData = [...activeItems, ...inActiveItems];

return sortedData;
}
10 changes: 9 additions & 1 deletion src/utils/dates.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ export function getFutureDate(offset) {

// transforms Firebase date object to JS date object
export function transformToJSDate(date) {
return date?.toDate();
if (date) {
const jsFullDate = date?.toDate();
const month = jsFullDate?.getMonth();
const day = jsFullDate?.getDate();
const year = jsFullDate?.getFullYear();
return new Date(year, month, day);
} else {
return null;
}
}

/**
Expand Down
35 changes: 28 additions & 7 deletions src/utils/items.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,40 @@ export function getItemDaysUntilNextPurchase(item) {

export function getItemDaysSinceLastPurchase(item) {
const today = new Date();
const lastPurchase = transformToJSDate(item.dateLastPurchased);
const lastPurchase = transformToJSDate(
item.dateLastPurchased || item.dateCreated,
);

return getDaysBetweenDates(lastPurchase, today);
}

export function sortItems(data) {
return data.sort((a, b) => {
const daysUntilNextPurchaseA = getItemDaysUntilNextPurchase(a);
const daysUntilNextPurchaseB = getItemDaysUntilNextPurchase(b);
const currentDate = transformToJSDate(a.dateNextPurchased);
const nextDate = transformToJSDate(b.dateNextPurchased);

return (
daysUntilNextPurchaseA - daysUntilNextPurchaseB ||
a.name.localeCompare(b.name)
);
if (currentDate > nextDate) {
return 1;
} else if (currentDate < nextDate) {
return -1;
} else {
return a.name.localeCompare(b.name);
}
});
}

export function comparePurchaseUrgency(data) {
const inactiveItems = sortItems(
data.filter((item) => {
return getItemDaysSinceLastPurchase(item) >= 60;
}),
);

const activeItems = sortItems(
data.filter((item) => {
return getItemDaysSinceLastPurchase(item) < 60;
}),
);

return [...activeItems, ...inactiveItems];
}
2 changes: 1 addition & 1 deletion src/views/List.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ListItem } from '../components';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { comparePurchaseUrgency } from '../api/firebase';
import { comparePurchaseUrgency } from '../utils/items';
import Chippy3 from '/img/Chippy3.gif';
import './List.css';

Expand Down
70 changes: 69 additions & 1 deletion tests/dates.test.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,32 @@
import { getDaysBetweenDates } from '../src/utils';
import {
getDaysBetweenDates,
transformToJSDate,
getNextPurchaseDate,
getFutureDate,
} from '../src/utils';
import { Timestamp } from 'firebase/firestore';

// Tests for transformToJSDate

describe('transforms a firestore object into a javascript date object', () => {
it('receives a firestore date, then converts that date into a javascript date', () => {
let firestoreDate = new Timestamp();
let jsDate = firestoreDate.toDate();

expect(transformToJSDate(firestoreDate)).toEqual(jsDate);
});
it('receives a firestore date object, then returns a javascript date object', () => {
let firestoreDate = new Timestamp();
let jsDate = firestoreDate.toDate();

expect(typeof transformToJSDate(firestoreDate)).toEqual(typeof jsDate);
});
it('receives a null, then it returns null', () => {
expect(transformToJSDate(null)).toEqual(null);
});
});

// Tests for getDaysBetweenDates function

describe('calculates days between dates correctly', () => {
it('given two consecutive dates, then one day is returned', () => {
Expand All @@ -15,3 +43,43 @@ describe('calculates days between dates correctly', () => {
expect(getDaysBetweenDates(date2, date1)).toEqual(1);
});
});

// Tests for getNextPurchaseDate function

describe('calculates the next day to purchase an item and returns a new date', () => {
const getNextDate = (daysToAdd) => {
const today = new Date();
const nextDate = new Date(today.setDate(today.getDate() + daysToAdd));
return nextDate;
};

it('receives 1 day from today and returns a date 1 day in the future', () => {
const expectedDate = getNextDate(1);
expect(getNextPurchaseDate(1)).toEqual(expectedDate);
});

it('receives a large number of days from today and returns the correct month for the date', () => {
const expectedDate = getNextDate(55);
expect(getNextPurchaseDate(55)).toEqual(expectedDate);
});

it('returns the same date if 0 days are passed', () => {
const expectedDate = getNextDate(0);
expect(getNextPurchaseDate(0)).toEqual(expectedDate);
});
});

// Tests for getFutureDate function
describe('getFutureDate', () => {
const ONE_DAY_IN_MILLISECONDS = 86400000;
const newDateOffsetByNum = (num) =>
new Date(Date.now() + num * ONE_DAY_IN_MILLISECONDS);

it('receives a number of days and returns a date offset by that many days', () => {
expect(getFutureDate(3).date).toEqual(newDateOffsetByNum(3).date);
});

it('returns 1 day in the future if 0 days are passed', () => {
expect(getFutureDate(0).date).toEqual(newDateOffsetByNum(0).date);
});
});
205 changes: 205 additions & 0 deletions tests/items.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import {
getItemDaysUntilNextPurchase,
getItemDaysSinceLastPurchase,
sortItems,
comparePurchaseUrgency,
} from '../src/utils';
import { Timestamp } from 'firebase/firestore';

// tests for getItemDaysUntilNextPurchase function

describe('uses getDaysBetweenDates function to calculate number of days between today and the dateNextPurchased property of an item', () => {
const getNextDate = (daysToAdd) => {
const today = new Date();
const nextDate = new Date(today.setDate(today.getDate() + daysToAdd));
return nextDate;
};
it('receives an item with a date property of today and calculates the number of days between today and the received date', () => {
const item = {
dateNextPurchased: Timestamp.fromDate(new Date(getNextDate(20))),
};
expect(getItemDaysUntilNextPurchase(item)).toEqual(19);
});
it('receives an item with a date property 30 days in the future and returns the number of days between today and that date, not counting today', () => {
let item = {
dateNextPurchased: Timestamp.fromDate(new Date(getNextDate(30))),
};
expect(getItemDaysUntilNextPurchase(item)).toEqual(29);
});

it('receives an object and returns a number type', () => {
const item = {
dateNextPurchased: Timestamp.now(),
};
expect(getItemDaysUntilNextPurchase(item)).toEqual(Number());
});
});

// tests for getItemDaysSinceLastPurchase

describe('the getItemDaysSinceLastPurchase function receives an item object from the database and calculates the number of days between today and either the items dateLastPurchased date property or if no date last purchased then the items date created date property', () => {
const customFireStoreDate = (dateString) =>
Timestamp.fromDate(new Date(dateString));

const getPastDate = (daysToSubtract) => {
const today = new Date();
const pastDate = new Date(today.setDate(today.getDate() - daysToSubtract));
return pastDate;
};

it('given an item with a daysSinceLastPurchase property, then calculates the number of days between today and that date', () => {
const item = {
dateLastPurchased: customFireStoreDate(getPastDate(30)),
dateCreated: customFireStoreDate(getPastDate(60)),
};

expect(getItemDaysSinceLastPurchase(item)).toEqual(30);
});

it('given an item without a daysSinceLastPurchase property, then calculates the number of days between today and the dateCreated date property', () => {
const item = {
dateLastPurchased: null,
dateCreated: customFireStoreDate(getPastDate(60)),
};

expect(getItemDaysSinceLastPurchase(item)).toEqual(60);
});
});

// test for sortItems function
describe('sortItems', () => {
const customFireStoreDate = (dateString) =>
Timestamp.fromDate(new Date(dateString));

it('sorts the items based on the dateNextPurchased property and name', () => {
// Create sample data
const data = [
{
name: 'Apple',
dateNextPurchased: customFireStoreDate('2023-05-15'),
},
{
name: 'Banana',
dateNextPurchased: customFireStoreDate('2023-05-15'),
},
{
name: 'Ahi-Tuna',
dateNextPurchased: customFireStoreDate('2023-05-13'),
},
{
name: 'Coffee',
dateNextPurchased: customFireStoreDate('2023-05-14'),
},
{
name: 'Coconut',
dateNextPurchased: customFireStoreDate('2023-05-16'),
},
];

// Expected result after sorting
const expectedResult = [
{
name: 'Ahi-Tuna',
dateNextPurchased: customFireStoreDate('2023-05-13'),
},
{
name: 'Coffee',
dateNextPurchased: customFireStoreDate('2023-05-14'),
},
{
name: 'Apple',
dateNextPurchased: customFireStoreDate('2023-05-15'),
},
{
name: 'Banana',
dateNextPurchased: customFireStoreDate('2023-05-15'),
},
{
name: 'Coconut',
dateNextPurchased: customFireStoreDate('2023-05-16'),
},
];

// Sort the items
const sortedItems = sortItems(data);

// Compare the sorted items with the expected result
expect(sortedItems).toEqual(expectedResult);
});
});

// Tests for comparePurchaseUrgency

describe('the comparePurchaseUrgency function receives an array of objects and breaks it up into two separate arrays based on the dateLastPurchased property then concatenates the arrays back together', () => {
const customFireStoreDate = (dateString) =>
Timestamp.fromDate(new Date(dateString));

const getPastDate = (daysToSubtract) => {
const today = new Date();
const pastDate = new Date(today.setDate(today.getDate() - daysToSubtract));
return pastDate;
};

it('sorts the data into two groups based on whether the dateLastPurchased property is <= 60 days or not, then puts the groups back together in one array', () => {
const data = [
{
name: 'item2',
dateLastPurchased: customFireStoreDate(getPastDate(20)),
},
{
name: 'item5',
dateLastPurchased: customFireStoreDate(getPastDate(77)),
},
{
name: 'item1',
dateLastPurchased: customFireStoreDate(getPastDate(5)),
},
{
name: 'item3',
dateLastPurchased: customFireStoreDate(getPastDate(60)),
},
{
name: 'item4',
dateLastPurchased: customFireStoreDate(getPastDate(61)),
},
{
name: 'item6',
dateLastPurchased: customFireStoreDate(getPastDate(90)),
},
];

const expectedResult = [
{
name: 'item1',
dateLastPurchased: customFireStoreDate(getPastDate(5)),
},
{
name: 'item2',
dateLastPurchased: customFireStoreDate(getPastDate(20)),
},
{
name: 'item3',
dateLastPurchased: customFireStoreDate(getPastDate(60)),
},
{
name: 'item4',
dateLastPurchased: customFireStoreDate(getPastDate(61)),
},
{
name: 'item5',
dateLastPurchased: customFireStoreDate(getPastDate(77)),
},
{
name: 'item6',
dateLastPurchased: customFireStoreDate(getPastDate(90)),
},
];

const concatenatedDataMap = comparePurchaseUrgency(data).map(
(item) => item.name,
);
const expectedDataMap = expectedResult.map((item) => item.name);

expect(concatenatedDataMap).toEqual(expectedDataMap);
});
});

0 comments on commit 8a4fb92

Please sign in to comment.