Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions content/3.testing/.template/files/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts">
const msg = 'testing'
</script>

<template>
<h1>{{ msg }}</h1>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO: write unit tests for CountDown component
81 changes: 81 additions & 0 deletions content/3.testing/.template/files/components/CountDown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<script setup lang="ts">
// This component is a countdown timer for the end of the year.
// It will display the number of days, hours, minutes, and seconds until the end of the year.
// It also displays the number of weeks remaining in the year.

const countdown = ref({
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
})

const endOfYear = new Date(new Date().getFullYear() + 1, 0, 1)

function updateCountdown() {
const now = new Date()
const diff = endOfYear.getTime() - now.getTime()

const days = Math.floor(diff / (1000 * 60 * 60 * 24))
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
const seconds = Math.floor((diff % (1000 * 60)) / 1000)

countdown.value.days = days
countdown.value.hours = hours
countdown.value.minutes = minutes
countdown.value.seconds = seconds
}

let intervalId: NodeJS.Timeout

onMounted(() => {
// Call updateCountdown immediately to set the initial state
updateCountdown()

// Update the countdown every second
intervalId = setInterval(updateCountdown, 1000)
})

onUnmounted(() => {
// Clear the interval when the component is unmounted
clearInterval(intervalId)
})

const { totalWeeks, currentWeekNumber } = useWeek()
</script>

<template>
<div class="flex flex-col justify-between gap-x-16 gap-y-6 border-4 bg-white p-6 md:flex-row md:items-center dark:border-gray-600 dark:bg-gray-800" data-test-id="countdown-timer">
<div class="text-gray-800 dark:text-gray-200">
<h2 class="mb-2 text-lg text-gray-900 font-bold uppercase dark:text-gray-100">
Time Until New Year
</h2>
<p class="flex flex-wrap gap-2 text-xl font-bold tracking-wide md:text-2xl" data-test-id="countdown">
<span class="whitespace-nowrap">
<span class="tabular-nums" data-test-id="countdown-days">{{ countdown.days }}</span> days,
</span>
<span class="whitespace-nowrap">
<span class="tabular-nums" data-test-id="countdown-hours">{{ countdown.hours }}</span> hours,
</span>
<span class="whitespace-nowrap">
<span class="tabular-nums" data-test-id="countdown-minutes">{{ countdown.minutes }}</span> minutes and
</span>
<span class="whitespace-nowrap">
<span class="tabular-nums" data-test-id="countdown-seconds">{{ countdown.seconds }}</span> seconds
</span>
</p>
</div>
<div class="flex gap-1 text-gray-800 md:flex-col md:items-end dark:text-gray-200">
<div class="text-lg font-bold uppercase md:text-xl" data-test-id="weeks">
<div class="w-fit md:ml-auto">
<span class="text-yellow-700 dark:text-yellow-400" data-test-id="current-week">Week {{ currentWeekNumber }}</span>
<span class="text-gray-700 dark:text-gray-300" data-test-id="total-weeks"> of {{ totalWeeks }}</span>
</div>
<div class="whitespace-nowrap text-base text-gray-600 dark:text-gray-400" data-test-id="weeks-remaining">
({{ totalWeeks - currentWeekNumber }} weeks remaining)
</div>
</div>
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useWeek } from './useWeek'

describe('useWeek', () => {

Check failure on line 3 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.
beforeEach(() => {

Check failure on line 4 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'beforeEach'.
// Mock the current date to a fixed value for consistent testing
vi.useFakeTimers()

Check failure on line 6 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'vi'.
vi.setSystemTime(new Date('2024-03-14'))

Check failure on line 7 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'vi'.
})

afterEach(() => {

Check failure on line 10 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'afterEach'.
vi.useRealTimers()

Check failure on line 11 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'vi'.
})

it('initializes with current year', () => {

Check failure on line 14 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.
const { weeks, currentWeekNumber, totalWeeks } = useWeek()

expect(weeks.value.length).toBeGreaterThan(0)

Check failure on line 17 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'expect'.
expect(currentWeekNumber.value).toBe(11) // Week 11 in March 14, 2024

Check failure on line 18 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'expect'.
expect(totalWeeks.value).toBe(53)

Check failure on line 19 in content/3.testing/.template/files/composables/useWeek.nuxt.test.ts

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'expect'.
})

it('calculates remaining weeks correctly', () => {
const { remainingWeeks, currentWeekNumber, totalWeeks } = useWeek()

expect(remainingWeeks.value).toBe(totalWeeks.value - currentWeekNumber.value)
})

it('changes year correctly', () => {
const { changeYear, weeks, currentWeekNumber } = useWeek()

// Change to previous year
changeYear(2023)
expect(weeks.value.length).toBe(53)

expect(currentWeekNumber.value).toBe(0)
})

it('handles leap years correctly', () => {
const { changeYear, weeks } = useWeek()

// 2024 is a leap year
changeYear(2024)
expect(weeks.value.length).toBe(53)

// 2025 is not a leap year
changeYear(2025)
expect(weeks.value.length).toBe(53)
})
})
22 changes: 22 additions & 0 deletions content/3.testing/.template/files/composables/useWeek.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function useWeek() {
const yearRef = ref(new Date().getFullYear())
const weeks = computed(() => calculateWeeks(yearRef.value))

const currentWeekNumber = computed(() => weeks.value.find(week => week.isCurrentWeek)?.number ?? 0)

const totalWeeks = computed(() => weeks.value.length)

const remainingWeeks = computed(() => totalWeeks.value - currentWeekNumber.value)

const changeYear = (year: number) => {
yearRef.value = year
}

return {
weeks,
currentWeekNumber,
totalWeeks,
remainingWeeks,
changeYear,
}
}
5 changes: 5 additions & 0 deletions content/3.testing/.template/files/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default defineNuxtConfig({
modules: ['@nuxt/test-utils/module'],

compatibilityDate: '2025-02-25',
})
23 changes: 23 additions & 0 deletions content/3.testing/.template/files/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"type": "module",
"private": true,
"packageManager": "pnpm@9.15.4",
"scripts": {
"dev": "nuxt dev",
"prepare": "nuxt prepare",
"test:unit": "vitest",
"test:unit-coverage": "vitest --coverage",
"test:unit-ui": "vitest --coverage --ui"
},
"devDependencies": {
"@nuxt/test-utils": "latest",

Check failure on line 13 in content/3.testing/.template/files/package.json

View workflow job for this annotation

GitHub Actions / autofix

Catalog "catalog:" for package "@nuxt/test-utils" is not defined in `pnpm-workspace.yaml`
"@vitest/coverage-v8": "latest",

Check failure on line 14 in content/3.testing/.template/files/package.json

View workflow job for this annotation

GitHub Actions / autofix

Catalog "catalog:" for package "@vitest/coverage-v8" is not defined in `pnpm-workspace.yaml`
"@vitest/ui": "latest",

Check failure on line 15 in content/3.testing/.template/files/package.json

View workflow job for this annotation

GitHub Actions / autofix

Catalog "catalog:" for package "@vitest/ui" is not defined in `pnpm-workspace.yaml`
"@vue/test-utils": "latest",

Check failure on line 16 in content/3.testing/.template/files/package.json

View workflow job for this annotation

GitHub Actions / autofix

Catalog "catalog:" for package "@vue/test-utils" is not defined in `pnpm-workspace.yaml`
"happy-dom": "latest",

Check failure on line 17 in content/3.testing/.template/files/package.json

View workflow job for this annotation

GitHub Actions / autofix

Catalog "catalog:" for package "happy-dom" is not defined in `pnpm-workspace.yaml`
"nuxt": "latest",
"typescript": "latest",
"vitest": "latest",

Check failure on line 20 in content/3.testing/.template/files/package.json

View workflow job for this annotation

GitHub Actions / autofix

Catalog "catalog:" for package "vitest" is not defined in `pnpm-workspace.yaml`
"vue": "latest"
}
}
48 changes: 48 additions & 0 deletions content/3.testing/.template/files/utils/TimeUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { calculateWeeks } from './TimeUtils'

describe('timeUtils', () => {
beforeEach(() => {
// Set fixed date for consistent testing
vi.useFakeTimers()
vi.setSystemTime(new Date('2024-03-14T12:00:00.000Z'))
})

afterEach(() => {
vi.useRealTimers()
})

describe('calculateWeeks', () => {
it('calculates correct number of weeks for a year', () => {
// Test a regular year (2023)
const weeks2023 = calculateWeeks(2023)
expect(weeks2023.length).toBe(53)

// Test a leap year (2024)
const weeks2024 = calculateWeeks(2024)
expect(weeks2024.length).toBe(53)
})

it('sets correct start and end dates for weeks', () => {
const weeks = calculateWeeks(2024)
const firstWeek = weeks[0]
const lastWeek = weeks[weeks.length - 1]

// Check date components separately to avoid timezone issues
expect(firstWeek?.weekStart.getFullYear()).toBe(2024)
expect(firstWeek?.weekStart.getMonth()).toBe(0) // January
expect(firstWeek?.weekStart.getDate()).toBe(1) // 1st

expect(lastWeek?.weekEnd.getFullYear()).toBe(2024)
expect(lastWeek?.weekEnd.getMonth()).toBe(11) // December
expect(lastWeek?.weekEnd.getDate()).toBe(31) // 31st
})

it('marks current week correctly', () => {
const weeks = calculateWeeks(2024)
const currentWeek = weeks.find(week => week.isCurrentWeek)

expect(currentWeek).toBeDefined()
expect(currentWeek?.number).toBe(11) // March 14, 2024 is in week 11
})
})
})
50 changes: 50 additions & 0 deletions content/3.testing/.template/files/utils/TimeUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export interface Week {
number: number
weekStart: Date
weekEnd: Date
isCurrentWeek: boolean
}

// Function to calculate weeks with start and end dates
// This function calculates all weeks for a given year and returns an array of Week objects
// Each Week object contains:
// - number: The week number (1-53)
// - weekStart: Date object for the start of week (Jan 1st for first week)
// - weekEnd: Date object for end of week (Dec 31st for last week)
// - isCurrentWeek: Boolean indicating if this is the current week
export function calculateWeeks(year: number): Week[] {
const today = new Date() // Get current date for comparing current week
const weeks: Week[] = []

// Start from January 1st of the selected year
const date = new Date(year, 0, 1)

// Calculate weeks by iterating through the year
let weekNumber = 1
while (date.getFullYear() === year) {
// Start of week is the current date
const weekStart = new Date(date)

// Move date to the end of the week (next Sunday)
// For first week, this could be less than 7 days if year starts mid-week
date.setDate(date.getDate() + ((7 - date.getDay()) % 7))

// Handle edge case for year boundary
const weekEnd
= date.getFullYear() !== year
? new Date(year, 11, 31, 23, 59, 59) // December 31st 23:59:59
: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)

// Check if this week contains today's date
const isCurrentWeek = today >= weekStart && today <= weekEnd

// Add the week to our array
weeks.push({ number: weekNumber, weekStart, weekEnd, isCurrentWeek })

// Move to start of next week
date.setDate(date.getDate() + 1)
weekNumber++
}

return weeks
}
15 changes: 15 additions & 0 deletions content/3.testing/.template/files/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineVitestConfig } from '@nuxt/test-utils/config'
import 'dotenv/config'

export default defineVitestConfig({
test: {
name: 'unit',
globals: true,
environment: 'node',
include: ['**/tests/unit/**/*.test.ts', '**/components/**/*.test.ts', '**/composables/**/*.test.ts', '**/utils/**/*.test.ts'],
coverage: {
provider: 'v8',
include: ['**/components/**/*.vue', '**/composables/**/*.ts'],
},
},
})
10 changes: 10 additions & 0 deletions content/3.testing/.template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { GuideMeta } from '~/types/guides'

export const meta: GuideMeta = {
startingFile: 'app.vue',
features: {
terminal: false,
fileTree: true,
navigation: false,
},
}
7 changes: 7 additions & 0 deletions content/3.testing/.template/solutions/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts">
const msg = 'testing'
</script>

<template>
<h1>{{ msg }}</h1>
</template>
Loading