Initial commit
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('simple connectivity test', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/');
|
||||
console.log('Page title:', await page.title());
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('page loads without critical errors', async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') {
|
||||
errors.push(msg.text());
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('http://localhost:5173/');
|
||||
await expect(page).toHaveTitle('Baby Sleep Tracker');
|
||||
|
||||
const awakeBanner = page.locator('text=Baby is awake');
|
||||
const sleepingBanner = page.locator('text=Baby is sleeping');
|
||||
const bannerVisible = await awakeBanner.or(sleepingBanner).isVisible();
|
||||
expect(bannerVisible).toBe(true);
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.log('Console errors:', errors);
|
||||
}
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Stats Tab', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/stats');
|
||||
await page.waitForTimeout(1000);
|
||||
});
|
||||
|
||||
test('1. Stats tab is reachable via tab navigation', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/');
|
||||
await page.getByRole('link', { name: /Stats/i }).click();
|
||||
await page.waitForTimeout(500);
|
||||
await expect(page.getByRole('heading', { name: /Sleep Statistics/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('2. Interval picker changes interval', async ({ page }) => {
|
||||
await expect(page.getByRole('button', { name: /7d/i })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: /14d/i }).click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.getByRole('button', { name: /14d/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('3. Timetable renders rows for selected interval', async ({ page }) => {
|
||||
const timetable = page.locator('text=Sleep Timetable');
|
||||
if (await timetable.isVisible({ timeout: 1000 }).catch(() => false)) {
|
||||
await expect(page.locator('text=00:00').first()).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('4. Wakeup and bedtime plots render SVG lines', async ({ page }) => {
|
||||
await page.getByRole('heading', { name: /Wakeup Times/i }).waitFor({ timeout: 2000 });
|
||||
await expect(page.locator('h3:has-text("Wakeup Times")')).toBeVisible();
|
||||
|
||||
await page.locator('h3:has-text("Bedtime")').first().waitFor({ timeout: 2000 });
|
||||
await expect(page.locator('h3:has-text("Bedtime")').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('5. Settings popover for day/night boundary saves to localStorage and re-fetches', async ({ page }) => {
|
||||
await page.getByRole('button', { name: /Settings/i }).click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
await expect(page.locator('text=Day starts')).toBeVisible();
|
||||
|
||||
await page.getByLabel('Day starts (HH:MM)').fill('08:00');
|
||||
await page.getByRole('button', { name: /Save/i }).click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const localStorage = await page.evaluate(() => {
|
||||
return window.localStorage.getItem('baby-sleep-settings');
|
||||
});
|
||||
expect(localStorage).toContain('08:00');
|
||||
});
|
||||
|
||||
test('6. Daily Summary shows chart with bars', async ({ page }) => {
|
||||
await expect(page.locator('text=Daily Summary')).toBeVisible();
|
||||
|
||||
const chartBars = page.locator('.recharts-bar-rectangle');
|
||||
if (await chartBars.first().isVisible({ timeout: 1000 }).catch(() => false)) {
|
||||
await expect(chartBars.first()).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('7. Average summaries display below chart', async ({ page }) => {
|
||||
const avgTotal = page.locator('text=Avg Total');
|
||||
if (await avgTotal.isVisible({ timeout: 1000 }).catch(() => false)) {
|
||||
await expect(avgTotal).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('stop sleep gives 422', async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
page.on('response', res => {
|
||||
if (res.url().includes('/api/sessions/') && res.status() >= 400) {
|
||||
errors.push(`${res.status()} at ${res.url()}`);
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('http://localhost:5173/');
|
||||
|
||||
await page.getByRole('button', { name: /Start Sleep/i }).click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.getByRole('button', { name: /Stop Sleep/i }).click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
console.log('Errors:', errors);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Tracker Flow', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// 1. Navigate to the app
|
||||
await page.goto('http://localhost:5173/');
|
||||
|
||||
// 2. Cleanup: If there is an active session, end it.
|
||||
const stopBtn = page.getByRole('button', { name: /Stop Sleep/i });
|
||||
if (await stopBtn.isVisible({ timeout: 1000 }).catch(() => false)) {
|
||||
await stopBtn.click();
|
||||
// Wait for the session to end and button to return to "Start Sleep" or be visible again.
|
||||
await expect(page.getByRole('button', { name: /Start Sleep/i })).toBeVisible({ timeout: 5000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('1. page loads — Start Sleep button is visible', async ({ page }) => {
|
||||
// By default, there should be no active session (after cleanup), so "Baby is awake" should be visible.
|
||||
await expect(page.getByRole('button', { name: /Start Sleep/i })).toBeVisible();
|
||||
await expect(page.getByText(/Baby is awake/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('2. click Start Sleep — button changes to Stop Sleep, status banner updates', async ({ page }) => {
|
||||
const startBtn = page.getByRole('button', { name: /Start Sleep/i });
|
||||
await startBtn.click();
|
||||
|
||||
// The button will temporarily show "Processing..." (disabled).
|
||||
// We wait for it to transition to the active session state: "Stop Sleep".
|
||||
await expect(page.getByRole('button', { name: /Stop Sleep/i })).toBeVisible();
|
||||
await expect(page.getByText(/Baby is sleeping/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('3. adjust start time to 5 minutes ago — verify API receives adjusted time', async ({ page }) => {
|
||||
const datetimeInput = page.locator('input[type="datetime-local"]');
|
||||
|
||||
const now = new Date();
|
||||
const fiveMinAgo = new Date(now.getTime() - 5 * 60 * 1000);
|
||||
const adjustedTime = fiveMinAgo.toISOString().slice(0, 16);
|
||||
await datetimeInput.fill(adjustedTime);
|
||||
|
||||
await page.getByRole('button', { name: /Start Sleep/i }).click();
|
||||
|
||||
// Wait for transition to "Stop Sleep"
|
||||
await expect(page.getByRole('button', { name: /Stop Sleep/i })).toBeVisible();
|
||||
await expect(page.getByText(/Baby is sleeping/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('4. click Stop Sleep — button returns to Start Sleep', async ({ page }) => {
|
||||
// 1. Start a session
|
||||
await page.getByRole('button', { name: /Start Sleep/i }).click();
|
||||
await expect(page.getByRole('button', { name: /Stop Sleep/i })).toBeVisible();
|
||||
|
||||
// 2. Stop the session
|
||||
await page.getByRole('button', { name: /Stop Sleep/i }).click();
|
||||
// After clicking, it should return to "Start Sleep"
|
||||
await expect(page.getByRole('button', { name: /Start Sleep/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('5. reload page — UI correctly reflects state from API', async ({ page }) => {
|
||||
// 1. Start a session
|
||||
await page.getByRole('button', { name: /Start Sleep/i }).click();
|
||||
await expect(page.getByRole('button', { name: /Stop Sleep/i })).toBeVisible();
|
||||
|
||||
// 2. Reload the page
|
||||
await page.reload();
|
||||
|
||||
// 3. Check if state is persistent from API
|
||||
await expect(page.getByRole('button', { name: /Stop Sleep/i })).toBeVisible();
|
||||
await expect(page.getByText(/Baby is sleeping/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('6. Reset to now link resets the time field', async ({ page }) => {
|
||||
const datetimeInput = page.locator('input[type="datetime-local"]');
|
||||
|
||||
// Fill with a dummy value to see if it resets
|
||||
const futureTime = new Date(Date.now() + 10 * 60 * 1000).toISOString().slice(0, 16);
|
||||
await datetimeInput.fill(futureTime);
|
||||
|
||||
// Click Reset
|
||||
await page.getByRole('button', { name: /Reset to now/i }).click();
|
||||
|
||||
// Value should be updated (might slightly differ due to seconds but we check visibility/existence)
|
||||
const newValue = await datetimeInput.inputValue();
|
||||
expect(newValue).not.toBe(futureTime);
|
||||
});
|
||||
|
||||
test('7. elapsed time shows for active session', async ({ page }) => {
|
||||
await page.getByRole('button', { name: /Start Sleep/i }).click();
|
||||
await expect(page.getByRole('button', { name: /Stop Sleep/i })).toBeVisible();
|
||||
|
||||
// Verify that elapsed time (e.g., "1m" or "just now") is visible
|
||||
await expect(page.getByText(/(\d+h|\d+m|just\s*now)/i)).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
import { test, expect } from 'vitest';
|
||||
|
||||
test('simple test', () => {
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import SleepButton from '../../src/components/tracker/SleepButton';
|
||||
import { useSleepSession } from '../../src/hooks/useSleepSession';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
vi.mock('../../src/hooks/useSleepSession', () => ({
|
||||
useSleepSession: vi.fn(),
|
||||
}));
|
||||
|
||||
const createTestQueryClient = () => new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { retry: false },
|
||||
},
|
||||
});
|
||||
|
||||
describe('SleepButton', () => {
|
||||
let queryClient: any;
|
||||
|
||||
beforeEach(() => {
|
||||
queryClient = createTestQueryClient();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const renderWithProviders = (ui: React.ReactElement) => {
|
||||
return render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ui}
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
it('renders "Start Sleep" when no active session', async () => {
|
||||
(useSleepSession as any).mockReturnValue({
|
||||
activeSession: null,
|
||||
isCreating: false,
|
||||
isEnding: false,
|
||||
});
|
||||
|
||||
renderWithProviders(<SleepButton time="2024-01-01T12:00" />);
|
||||
expect(screen.getByRole('button', { name: /Start Sleep/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders "Stop Sleep" when session is active', async () => {
|
||||
(useSleepSession as any).mockReturnValue({
|
||||
activeSession: { id: 1, start_time: '2024-01-01T12:00' },
|
||||
isCreating: false,
|
||||
isEnding: false,
|
||||
});
|
||||
|
||||
renderWithProviders(<SleepButton time="2024-01-01T12:00" />);
|
||||
expect(screen.getByRole('button', { name: /Stop Sleep/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls endSession when session is active and button is clicked', async () => {
|
||||
const endSessionMock = vi.fn();
|
||||
(useSleepSession as any).mockReturnValue({
|
||||
activeSession: { id: 1, start_time: '2024-01-01T12:00' },
|
||||
isCreating: false,
|
||||
isEnding: false,
|
||||
endSession: endSessionMock,
|
||||
});
|
||||
|
||||
renderWithProviders(<SleepButton time="2024-01-01T14:00" />);
|
||||
const button = screen.getByRole('button', { name: /Stop Sleep/i });
|
||||
fireEvent.click(button);
|
||||
|
||||
await waitFor(() => expect(endSessionMock).toHaveBeenCalled());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import TimeAdjuster from '../../src/components/tracker/TimeAdjuster';
|
||||
|
||||
describe('TimeAdjuster', () => {
|
||||
it('renders with default value', () => {
|
||||
const { container } = render(<TimeAdjuster onTimeChange={() => {}} />);
|
||||
const input = container.querySelector('input');
|
||||
expect(input).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onTimeChange when value changes', () => {
|
||||
const handleChange = vi.fn();
|
||||
const { container } = render(<TimeAdjuster onTimeChange={handleChange} />);
|
||||
const input = container.querySelector('input');
|
||||
|
||||
fireEvent.change(input!, { target: { value: '2024-01-01T12:00' } });
|
||||
expect(handleChange).toHaveBeenCalledWith('2024-01-01T12:00');
|
||||
});
|
||||
|
||||
it('resets to now when reset button is clicked', async () => {
|
||||
const handleChange = vi.fn();
|
||||
render(<TimeAdjuster onTimeChange={handleChange} />);
|
||||
const resetButton = screen.getByText(/Reset to now/i);
|
||||
|
||||
fireEvent.click(resetButton);
|
||||
expect(handleChange).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
buildTimetableSlots,
|
||||
computeWakeupMinutes,
|
||||
computeBedtimeMinutes,
|
||||
computeDailySummaryData,
|
||||
computeAverageStats,
|
||||
reverseDateOrder,
|
||||
filterDaysWithData,
|
||||
} from '../../src/utils/chartHelpers';
|
||||
import type { TimetableSlot, WakeupBedtimeDay, DailySummaryDay } from '../../src/types';
|
||||
|
||||
describe('buildTimetableSlots', () => {
|
||||
it('groups slots by date', () => {
|
||||
const slots: TimetableSlot[] = [
|
||||
{ date: '2024-01-15', slot_start: '00:00', slot_end: '00:15', sleeping: true },
|
||||
{ date: '2024-01-15', slot_start: '00:15', slot_end: '00:30', sleeping: false },
|
||||
{ date: '2024-01-16', slot_start: '00:00', slot_end: '00:15', sleeping: true },
|
||||
];
|
||||
const result = buildTimetableSlots(slots);
|
||||
expect(Object.keys(result)).toHaveLength(2);
|
||||
expect(result['2024-01-15']).toHaveLength(2);
|
||||
expect(result['2024-01-16']).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeWakeupMinutes', () => {
|
||||
it('transforms wakeup data for chart', () => {
|
||||
const days: WakeupBedtimeDay[] = [
|
||||
{ date: '2024-01-15', wakeup_minutes: 195, bedtime_minutes: 75 },
|
||||
];
|
||||
const result = computeWakeupMinutes(days, '04:00');
|
||||
expect(result[0].time).toBe('07:15');
|
||||
expect(result[0].minutes).toBe(195);
|
||||
});
|
||||
|
||||
it('handles null values', () => {
|
||||
const days: WakeupBedtimeDay[] = [
|
||||
{ date: '2024-01-15', wakeup_minutes: null, bedtime_minutes: null },
|
||||
];
|
||||
const result = computeWakeupMinutes(days, '04:00');
|
||||
expect(result[0].time).toBe('N/A');
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeBedtimeMinutes', () => {
|
||||
it('transforms bedtime data for chart', () => {
|
||||
const days: WakeupBedtimeDay[] = [
|
||||
{ date: '2024-01-15', wakeup_minutes: 195, bedtime_minutes: 75 },
|
||||
];
|
||||
const result = computeBedtimeMinutes(days, '19:00');
|
||||
expect(result[0].time).toBe('20:15');
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeDailySummaryData', () => {
|
||||
it('transforms daily summary for BarChart', () => {
|
||||
const days: DailySummaryDay[] = [
|
||||
{ date: '2024-01-15', total_minutes: 720, day_minutes: 120, night_minutes: 600 },
|
||||
];
|
||||
const result = computeDailySummaryData(days);
|
||||
expect(result[0]).toEqual({
|
||||
date: '2024-01-15',
|
||||
daySleep: 120,
|
||||
nightSleep: 600,
|
||||
total: 720,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeAverageStats', () => {
|
||||
it('calculates averages from daily data', () => {
|
||||
const days: DailySummaryDay[] = [
|
||||
{ date: '2024-01-15', total_minutes: 720, day_minutes: 120, night_minutes: 600 },
|
||||
{ date: '2024-01-16', total_minutes: 600, day_minutes: 100, night_minutes: 500 },
|
||||
];
|
||||
const result = computeAverageStats(days);
|
||||
expect(result.avgTotal).toBe(660);
|
||||
expect(result.avgDay).toBe(110);
|
||||
expect(result.avgNight).toBe(550);
|
||||
expect(result.count).toBe(2);
|
||||
});
|
||||
|
||||
it('handles empty array', () => {
|
||||
const result = computeAverageStats([]);
|
||||
expect(result.avgTotal).toBe(0);
|
||||
expect(result.count).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reverseDateOrder', () => {
|
||||
it('reverses array order', () => {
|
||||
const days = [
|
||||
{ date: '2024-01-16' },
|
||||
{ date: '2024-01-15' },
|
||||
{ date: '2024-01-14' },
|
||||
];
|
||||
const result = reverseDateOrder(days);
|
||||
expect(result[0].date).toBe('2024-01-14');
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterDaysWithData', () => {
|
||||
it('filters days with zero total', () => {
|
||||
const days = [
|
||||
{ date: '2024-01-15', total_minutes: 720 },
|
||||
{ date: '2024-01-16', total_minutes: 0 },
|
||||
];
|
||||
const result = filterDaysWithData(days);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].date).toBe('2024-01-15');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,106 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
formatMinutesAfter,
|
||||
toLocalDatetimeInputValue,
|
||||
formatElapsedTime,
|
||||
clampToDay,
|
||||
parseTime,
|
||||
formatMinutes,
|
||||
getDaysAgo,
|
||||
getToday,
|
||||
} from '../../src/utils/time';
|
||||
|
||||
describe('formatMinutesAfter', () => {
|
||||
it('converts positive minutes after reference', () => {
|
||||
expect(formatMinutesAfter(75, '04:00')).toBe('05:15');
|
||||
});
|
||||
|
||||
it('handles zero minutes', () => {
|
||||
expect(formatMinutesAfter(0, '04:00')).toBe('04:00');
|
||||
});
|
||||
|
||||
it('wraps around midnight for large values', () => {
|
||||
expect(formatMinutesAfter(480, '04:00')).toBe('12:00');
|
||||
});
|
||||
|
||||
it('handles null input', () => {
|
||||
expect(formatMinutesAfter(null, '04:00')).toBe('N/A');
|
||||
});
|
||||
|
||||
it('handles negative minutes', () => {
|
||||
expect(formatMinutesAfter(-30, '04:00')).toBe('03:30');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toLocalDatetimeInputValue', () => {
|
||||
it('converts Date to datetime-local format', () => {
|
||||
const date = new Date('2024-01-15T21:30:00Z');
|
||||
const result = toLocalDatetimeInputValue(date);
|
||||
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatElapsedTime', () => {
|
||||
it('formats hours and minutes', () => {
|
||||
const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
|
||||
expect(formatElapsedTime(twoHoursAgo)).toBe('2h');
|
||||
});
|
||||
|
||||
it('formats minutes only', () => {
|
||||
const thirtyMinsAgo = new Date(Date.now() - 30 * 60 * 1000).toISOString();
|
||||
expect(formatElapsedTime(thirtyMinsAgo)).toBe('30m');
|
||||
});
|
||||
});
|
||||
|
||||
describe('clampToDay', () => {
|
||||
it('clamps to start of day', () => {
|
||||
const date = new Date('2024-01-15T15:30:00');
|
||||
const result = clampToDay(date, 'start');
|
||||
expect(result.getHours()).toBe(0);
|
||||
expect(result.getMinutes()).toBe(0);
|
||||
});
|
||||
|
||||
it('clamps to end of day', () => {
|
||||
const date = new Date('2024-01-15T15:30:00');
|
||||
const result = clampToDay(date, 'end');
|
||||
expect(result.getHours()).toBe(23);
|
||||
expect(result.getMinutes()).toBe(59);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseTime', () => {
|
||||
it('parses HH:MM string to Date', () => {
|
||||
const result = parseTime('07:30');
|
||||
expect(result.getHours()).toBe(7);
|
||||
expect(result.getMinutes()).toBe(30);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatMinutes', () => {
|
||||
it('formats hours and minutes', () => {
|
||||
expect(formatMinutes(90)).toBe('1h 30m');
|
||||
});
|
||||
|
||||
it('formats hours only', () => {
|
||||
expect(formatMinutes(120)).toBe('2h');
|
||||
});
|
||||
|
||||
it('formats minutes only', () => {
|
||||
expect(formatMinutes(45)).toBe('45m');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDaysAgo', () => {
|
||||
it('returns date string for N days ago', () => {
|
||||
const result = getDaysAgo(7);
|
||||
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getToday', () => {
|
||||
it('returns today date string', () => {
|
||||
const result = getToday();
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
expect(result).toBe(today);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user