diff --git a/.github/workflows/update-version-from-comment.yml b/.github/workflows/update-version-from-comment.yml index 46cfc93..7923107 100644 --- a/.github/workflows/update-version-from-comment.yml +++ b/.github/workflows/update-version-from-comment.yml @@ -278,7 +278,7 @@ jobs: fi git add CHANGELOG.md package.json - git commit -m "docs: update CHANGELOG.md and package.json for v${UPDATE_VERSION}" + git commit -m "release: v${UPDATE_VERSION}" git push origin auto-changelog-update diff --git a/README.md b/README.md index 98c4506..8d78773 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # shared +[![Release & Publish](https://github.com/ummgoban/shared/actions/workflows/create-release.yml/badge.svg)](https://github.com/ummgoban/shared/actions/workflows/create-release.yml) +[![Check](https://github.com/ummgoban/shared/actions/workflows/check.yml/badge.svg)](https://github.com/ummgoban/shared/actions/workflows/check.yml) + ummgoban 공통 패키지입니다. - ummgoban 도메인에서 공용으로 사용하는 type, utils, http client, hook 등을 관리합니다. diff --git a/package.json b/package.json index 13661b7..33cd64b 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "type": "git", "url": "https://github.com/ummgoban/shared.git" }, + "homepage": "https://github.com/ummgoban/shared#readme", "author": "ummgoban", "license": "MIT", "peerDependencies": { diff --git a/src/lib/constants/weekday.const.ts b/src/lib/constants/weekday.const.ts index cb0f7bf..0790624 100644 --- a/src/lib/constants/weekday.const.ts +++ b/src/lib/constants/weekday.const.ts @@ -16,6 +16,8 @@ export const dayMap: Record = { * @description * - 상수로 정의된 요일을 enum으로 정의 * - 날짜간 대소 비교에 사용 + * - `WeekdayEnum["MONDAY"] === 0` + * * @example * ```ts * const mondayString: Weekday = "MONDAY" diff --git a/src/lib/index.ts b/src/lib/index.ts index 5aca993..518690f 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,3 +1,4 @@ export * from './constants'; export * from './types'; export * from './utils'; +export * from './model'; diff --git a/src/lib/model/Market.model.spec.ts b/src/lib/model/Market.model.spec.ts new file mode 100644 index 0000000..e8c18ff --- /dev/null +++ b/src/lib/model/Market.model.spec.ts @@ -0,0 +1,194 @@ +import {Market} from './Market.model'; +import {MarketType} from '../types/market.type'; + +class MockDate extends Date { + private mockDay: number; + private mockHours: number; + private mockMinutes: number; + + constructor(day: number, hours: number, minutes: number) { + super(); + this.mockDay = day; + this.mockHours = hours; + this.mockMinutes = minutes; + } + + getDay(): number { + return this.mockDay; + } + + getHours(): number { + return this.mockHours; + } + + getMinutes(): number { + return this.mockMinutes; + } +} + +const market: Market = new Market({ + id: 1, + name: 'test', + openHours: [ + { + dayOfWeek: 'MONDAY', + openTime: '09:30', + closeTime: '18:00', + }, + { + dayOfWeek: 'TUESDAY', + openTime: '09:30', + closeTime: '18:00', + }, + { + dayOfWeek: 'WEDNESDAY', + openTime: '09:30', + closeTime: '18:00', + }, + { + dayOfWeek: 'THURSDAY', + openTime: '09:30', + closeTime: '18:00', + }, + { + dayOfWeek: 'FRIDAY', + openTime: '09:30', + closeTime: '18:00', + }, + { + dayOfWeek: 'SATURDAY', + openTime: '09:30', + closeTime: '18:00', + }, + { + dayOfWeek: 'SUNDAY', + openTime: '09:30', + closeTime: '18:00', + }, + ], + address: 'test', + products: [], + imageUrls: [], + summary: 'test', +}); + +describe('Market', () => { + it('should be defined', () => { + expect(Market).toBeDefined(); + }); + + it('should isOpen return true when market is open', () => { + vitest.spyOn(global, 'Date').mockImplementation(() => new MockDate(0, 9, 30)); + + expect(market.isOpen()).toBeTruthy(); + vitest.restoreAllMocks(); + }); + + it('should isOpen return false when market is open (same open time)', () => { + vitest.spyOn(global, 'Date').mockImplementation(() => new MockDate(0, 9, 29)); + + expect(market.isOpen()).toBeFalsy(); + vitest.restoreAllMocks(); + }); + + it('should isOpen return false when market is open (same close time)', () => { + vitest.spyOn(global, 'Date').mockImplementation(() => new MockDate(0, 18, 0)); + + expect(market.isOpen()).toBeFalsy(); + vitest.restoreAllMocks(); + }); + + it('should isOpen return false when market is closed', () => { + vitest.spyOn(global, 'Date').mockImplementation(() => new MockDate(0, 19, 0)); + + expect(market.isOpen()).toBeFalsy(); + vitest.restoreAllMocks(); + }); + + it('should isOpen return false when market is not open day', () => { + // 금요일 10시 00분 + vitest.spyOn(global, 'Date').mockImplementation(() => new MockDate(5, 10, 0)); + + const closedMarket = new Market({ + id: 1, + name: 'test', + openHours: [ + { + dayOfWeek: 'MONDAY', + openTime: '09:00', + closeTime: '18:00', + }, + { + dayOfWeek: 'WEDNESDAY', + openTime: '09:00', + closeTime: '18:00', + }, + ], + address: 'test', + products: [], + imageUrls: [], + summary: 'test', + }); + + expect(closedMarket.isOpen()).toBeFalsy(); + vitest.restoreAllMocks(); + }); + + it('should compatible with MarketType (MarketType -> Market)', () => { + const marketType: MarketType = { + id: 1, + name: 'test', + openHours: [ + { + dayOfWeek: 'MONDAY', + openTime: '09:00', + closeTime: '18:00', + }, + { + dayOfWeek: 'TUESDAY', + openTime: '09:00', + closeTime: '18:00', + }, + ], + address: 'test', + products: [], + imageUrls: [], + summary: 'test', + }; + + const market = new Market(marketType); + + expect(market).toBeInstanceOf(Market); + expect(market.id).toBe(marketType.id); + expect(market.name).toBe(marketType.name); + expect(market.openHours).toEqual(marketType.openHours); + expect(market.address).toBe(marketType.address); + expect(market.products).toEqual(marketType.products); + expect(market.imageUrls).toEqual(marketType.imageUrls); + expect(market.summary).toBe(marketType.summary); + }); + + it('should compatible with MarketType (Market -> MarketType)', () => { + const market = new Market({ + id: 1, + name: 'test', + openHours: [], + address: 'test', + products: [], + imageUrls: [], + summary: 'test', + }); + + const marketType: MarketType = market; + + expect(marketType).toEqual({ + id: 1, + name: 'test', + openHours: [], + address: 'test', + products: [], + imageUrls: [], + summary: 'test', + }); + }); +}); diff --git a/src/lib/model/Market.model.ts b/src/lib/model/Market.model.ts new file mode 100644 index 0000000..9e357c4 --- /dev/null +++ b/src/lib/model/Market.model.ts @@ -0,0 +1,47 @@ +import {MarketType} from '../types/market.type'; +import {WeekdayEnum} from '../constants/weekday.const'; + +export class Market { + id: MarketType['id']; + name: MarketType['name']; + openHours: MarketType['openHours']; + address: MarketType['address']; + products: MarketType['products']; + imageUrls: MarketType['imageUrls']; + summary: MarketType['summary']; + + constructor(data: MarketType) { + this.id = data.id; + this.name = data.name; + this.openHours = data.openHours; + this.address = data.address; + this.products = data.products; + this.imageUrls = data.imageUrls; + this.summary = data.summary; + } + + public isOpen(): boolean { + const now = new Date(); + const dayOfWeek = now.getDay(); + const openHour = this.openHours.find(openHour => WeekdayEnum[openHour.dayOfWeek] === (dayOfWeek + 1) % 7); + + if (!openHour) return false; + + const nowHour = now.getHours(); + const nowMinute = now.getMinutes(); + + const openTime = openHour.openTime; + const closeTime = openHour.closeTime; + + const [openTimeHour, openTimeMinute] = openTime.split(':').map(Number); + const [closeTimeHour, closeTimeMinute] = closeTime.split(':').map(Number); + + if (nowHour < openTimeHour || nowHour > closeTimeHour) return false; + + if (nowHour === openTimeHour && nowMinute < openTimeMinute) return false; + + if (nowHour === closeTimeHour && nowMinute >= closeTimeMinute) return false; + + return true; + } +} diff --git a/src/lib/model/index.ts b/src/lib/model/index.ts new file mode 100644 index 0000000..14ac28c --- /dev/null +++ b/src/lib/model/index.ts @@ -0,0 +1 @@ +export {Market} from './Market.model';