Skip to content

Commit

Permalink
Merge pull request #3 from gardusig/feat/tests
Browse files Browse the repository at this point in the history
Feat/tests
  • Loading branch information
gardusig authored Jun 19, 2024
2 parents 0f00fcd + 1675e4e commit bf8aa75
Show file tree
Hide file tree
Showing 13 changed files with 4,240 additions and 813 deletions.
2 changes: 1 addition & 1 deletion .claspignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# except the extensions…
!appsscript.json
!**/*.ts
!src/**/*.ts

# ignore even valid files if in…
.git/**
Expand Down
8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
transform: {
"^.+\\.ts$": "ts-jest",
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
};
4,762 changes: 3,975 additions & 787 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"scripts": {
"lint": "eslint './**/*.ts'",
"format": "eslint './**/*.ts' --fix"
"format": "eslint './**/*.ts' --fix",
"test": "jest"
},
"dependencies": {
"@types/google-apps-script": "^1.0.78"
Expand All @@ -15,6 +16,9 @@
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-unused-imports": "^3.0.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0"
"typescript": "^5.0.0",
"jest": "^29.0.0",
"ts-jest": "^29.0.0",
"@types/jest": "^29.0.0"
}
}
}
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Constants {
export namespace Constants {
export const folderPrefix = "self-management/death-note";
}
53 changes: 53 additions & 0 deletions src/cupid/dateservice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export namespace Cupid.DateService {
/**
* Enum representing days of the week.
*/
export enum DayOfWeek {
SUNDAY = 0,
MONDAY = 1,
TUESDAY = 2,
WEDNESDAY = 3,
THURSDAY = 4,
FRIDAY = 5,
SATURDAY = 6,
}

/**
* Calculates the week number of the current date based on the given base day. If the year starts after the base year, that is week 0. Week 1 starts after the first baseDay of the year.
* @param baseDay - The base day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday).
* @returns {number} - The week number.
*/
export function getWeekNumber(baseDay: DayOfWeek = DayOfWeek.MONDAY): number {
const daysSinceFirstBaseDay: number = getDaysSinceFirstBaseDay(baseDay);
return Math.floor(daysSinceFirstBaseDay / 7) + 1;
}

/**
* Retrieves the current date formatted as a string in "yyyy-MM-dd" format,
* adjusted to the script's time zone.
* @returns {string} The formatted date string.
*/
export function getFormattedDate(): string {
return Utilities.formatDate(
new Date(),
Session.getScriptTimeZone(),
"yyyy-MM-dd",
);
}

function getFirstBaseDayOfYear(year: number, baseDay: DayOfWeek): Date {
const date = new Date(year, 0, 1);
while (date.getDay() !== baseDay) {
date.setDate(date.getDate() - 1);
}
return date;
}

function getDaysSinceFirstBaseDay(baseDay: DayOfWeek): number {
const now: Date = new Date();
const year: number = now.getFullYear();
const firstBaseDay: Date = getFirstBaseDayOfYear(year, baseDay);
const dayDuration = 1000 * 3600 * 24;
return Math.floor((now.getTime() - firstBaseDay.getTime()) / dayDuration);
}
}
7 changes: 5 additions & 2 deletions src/journal/journal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
namespace Journal {
import { Constants } from "../constants";
import { Cupid } from "../cupid/dateservice";

export namespace Journal {
export function getFolderPath(): string {
const weekNumber = DateUtil.getWeekNumber();
const weekNumber = Cupid.DateService.getWeekNumber();
return `${Constants.folderPrefix}/week-${weekNumber}`;
}
}
5 changes: 4 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Cupid } from "./cupid/dateservice";
import { Journal } from "./journal/journal";

export function createDailyJournalDoc(): void {
const docName = DateUtil.getFormattedDate();
const docName = Cupid.DateService.getFormattedDate();
const folderPath = Journal.getFolderPath();
ToiletPaper.Tissuer.createRoll(docName, folderPath);
}
16 changes: 0 additions & 16 deletions src/util/date.util.ts

This file was deleted.

73 changes: 73 additions & 0 deletions test/cupid/dateservice.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Cupid } from "../../src/cupid/dateservice";
import { TestRunner } from "../testrunner";

TestRunner.runTestSuiteWithMock(
"Cupid.DateService",
weekNumberTestSuiteGenerator,
formattedDateTestSuiteGenerator,
);

function weekNumberTestSuiteGenerator(): TestRunner.TestSuite {
const testCases = [];
const startDate = new Date("2024-06-03T00:00:00-03:00"); // Monday
for (let weekCounter = 0; weekCounter < 3; weekCounter++) {
for (let weekDay = 0; weekDay < 7; weekDay++) {
const date = new Date(startDate);
date.setDate(date.getDate() + weekCounter * 7 + weekDay);
const expectedWeekNumber = 23 + weekCounter;
const todayMockedDate = date.toISOString();
testCases.push(
createWeekNumberTestCase(expectedWeekNumber, todayMockedDate),
);
}
}
return {
testName: "getWeekNumber",
testCases: testCases,
};
}

function formattedDateTestSuiteGenerator(): TestRunner.TestSuite {
const testCases = [];
for (let day = 1; day < 31; day += 1) {
const date = new Date(
`2024-06-${String(day).padStart(2, "0")}T00:00:00-03:00`,
);
const expectedFormattedDate = date.toISOString().split("T")[0];
testCases.push(
createFormattedDateTestCase(expectedFormattedDate, date.toISOString()),
);
}
return {
testName: "getFormattedDate",
testCases: testCases,
};
}

function createWeekNumberTestCase(
expectedWeekNumber: number,
mockedDate: string,
): TestRunner.TestCase {
return {
mockSettings: { todayDate: mockedDate },
testCaseName: `should produce week number '${expectedWeekNumber}' given date '${mockedDate}'`,
testFunction: () => {
const result = Cupid.DateService.getWeekNumber();
expect(result).toBe(expectedWeekNumber);
},
};
}

function createFormattedDateTestCase(
expectedFormattedDate: string,
mockedDate: string,
): TestRunner.TestCase {
return {
mockSettings: { todayDate: mockedDate },
testCaseName: `should produce formatted date '${expectedFormattedDate}' given date '${mockedDate}'`,
testFunction: () => {
const formattedDate = Cupid.DateService.getFormattedDate();
expect(formattedDate).toBe(expectedFormattedDate);
},
};
}
57 changes: 57 additions & 0 deletions test/testmock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export namespace TestMock {
export interface Settings {
todayDate: string;
}

export function setupMocks(settings: Settings): void {
setupDateMock(settings.todayDate);
setupSessionMock();
setupUtilitiesMock();
}

export function cleanupMocks(): void {
global.Date = OriginalDate;
global.Utilities = OriginalUtilities;
global.Session = OriginalSession;
}

function setupDateMock(todayDateMock: string): void {
global.Date = class extends Date {
constructor(...args: any[]) {
if (args.length === 0) {
super(todayDateMock);
} else {
super(...(args as ConstructorParameters<typeof Date>));
}
}
} as typeof Date;
}

function setupUtilitiesMock(): void {
global.Utilities = {
...OriginalUtilities,
formatDate: formatDateMock,
} as GoogleAppsScript.Utilities.Utilities;
}

function setupSessionMock(): void {
global.Session = {
...OriginalSession,
getScriptTimeZone: getScriptTimeZoneMock,
} as GoogleAppsScript.Base.Session;
}

const formatDateMock = jest.fn((date, _timeZone, _format) => {
const pad = (n: number) => (n < 10 ? "0" + n : n);
const yyyy = date.getFullYear();
const mm = pad(date.getMonth() + 1);
const dd = pad(date.getDate());
return `${yyyy}-${mm}-${dd}`;
});

const getScriptTimeZoneMock = jest.fn(() => "GMT-3");

const OriginalDate = global.Date;
const OriginalUtilities = global.Utilities;
const OriginalSession = global.Session;
}
51 changes: 51 additions & 0 deletions test/testrunner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { TestMock } from "./testmock";

export namespace TestRunner {
export function runTestSuiteWithMock(
testSuiteName: string,
...testSuiteGenerators: TestSuiteGenerator[]
): void {
describe(testSuiteName, () => {
for (const testSuiteGenerator of testSuiteGenerators) {
const testSuite = testSuiteGenerator();
describe(testSuite.testName, () => {
runTestCaseWithMock(...shuffleArray(testSuite.testCases));
});
}
});
}

export function runTestCaseWithMock(...testCases: TestCase[]): void {
testCases.forEach((testCase) => {
it(testCase.testCaseName, () => {
TestMock.setupMocks(testCase.mockSettings);
try {
testCase.testFunction();
} finally {
TestMock.cleanupMocks();
}
});
});
}

export interface TestSuite {
testName: string;
testCases: TestCase[];
}

export interface TestCase {
mockSettings: TestMock.Settings;
testFunction: () => void;
testCaseName: string;
}

type TestSuiteGenerator = () => TestSuite;

function shuffleArray<T>(array: T[]): T[] {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
}
7 changes: 5 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
"sourceMap": true,
"incremental": true,
"skipLibCheck": true,
"rootDir": "./src",
"outDir": "./dist",
"moduleResolution": "node",
"outDir": "./dist",
"esModuleInterop": true,
},
"include": [
"src/**/*.ts", "test/**/*.ts",
],
"exclude": [
"node_modules",
"dist"
Expand Down

0 comments on commit bf8aa75

Please sign in to comment.