| import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' |
| import { flushPromises, mount } from '@vue/test-utils' |
| import WeatherWidget from './WeatherWidget.vue' |
|
|
| describe('WeatherWidget', () => { |
| let originalGeolocation |
|
|
| beforeEach(() => { |
| originalGeolocation = navigator.geolocation |
| global.fetch = vi.fn() |
| }) |
|
|
| afterEach(() => { |
| if (originalGeolocation === undefined) delete navigator.geolocation |
| else Object.defineProperty(navigator, 'geolocation', { value: originalGeolocation, configurable: true }) |
| }) |
|
|
| it('shows the denied state when geolocation permission is refused', async () => { |
| Object.defineProperty(navigator, 'geolocation', { |
| value: { getCurrentPosition: (_res, rej) => rej(new Error('denied')) }, |
| configurable: true, |
| }) |
| const wrapper = mount(WeatherWidget) |
| await flushPromises() |
| expect(wrapper.text()).toContain('Enable location access') |
| }) |
|
|
| it('shows weather details once loaded successfully', async () => { |
| Object.defineProperty(navigator, 'geolocation', { |
| value: { |
| getCurrentPosition: (res) => res({ coords: { latitude: 1, longitude: 2 } }), |
| }, |
| configurable: true, |
| }) |
| global.fetch |
| .mockResolvedValueOnce({ |
| json: async () => ({ |
| current: { |
| temperature_2m: 21.4, |
| apparent_temperature: 20.1, |
| relative_humidity_2m: 55, |
| wind_speed_10m: 12.6, |
| weather_code: 0, |
| is_day: 1, |
| }, |
| }), |
| }) |
| .mockResolvedValueOnce({ |
| json: async () => ({ address: { city: 'Springfield', country_code: 'us' } }), |
| }) |
|
|
| const wrapper = mount(WeatherWidget) |
| await flushPromises() |
| expect(wrapper.text()).toContain('21°') |
| expect(wrapper.text()).toContain('Springfield') |
| expect(wrapper.text()).toContain('US') |
| expect(wrapper.text()).toContain('Clear sky') |
| }) |
|
|
| it('falls back to an unknown weather label for an unrecognised code', async () => { |
| Object.defineProperty(navigator, 'geolocation', { |
| value: { getCurrentPosition: (res) => res({ coords: { latitude: 1, longitude: 2 } }) }, |
| configurable: true, |
| }) |
| global.fetch |
| .mockResolvedValueOnce({ |
| json: async () => ({ |
| current: { |
| temperature_2m: 10, apparent_temperature: 9, relative_humidity_2m: 40, |
| wind_speed_10m: 5, weather_code: 999, is_day: 0, |
| }, |
| }), |
| }) |
| .mockResolvedValueOnce({ json: async () => ({ address: {} }) }) |
|
|
| const wrapper = mount(WeatherWidget) |
| await flushPromises() |
| expect(wrapper.text()).toContain('Unknown') |
| expect(wrapper.text()).toContain('Your location') |
| }) |
|
|
| it('shows an error state when the weather fetch fails', async () => { |
| Object.defineProperty(navigator, 'geolocation', { |
| value: { getCurrentPosition: (res) => res({ coords: { latitude: 1, longitude: 2 } }) }, |
| configurable: true, |
| }) |
| global.fetch.mockRejectedValue(new Error('network down')) |
| const wrapper = mount(WeatherWidget) |
| await flushPromises() |
| expect(wrapper.text()).toContain("Couldn't load weather") |
| }) |
|
|
| it('retries loading when the retry button is clicked', async () => { |
| Object.defineProperty(navigator, 'geolocation', { |
| value: { getCurrentPosition: (_res, rej) => rej(new Error('denied')) }, |
| configurable: true, |
| }) |
| const wrapper = mount(WeatherWidget) |
| await flushPromises() |
| await wrapper.find('button').trigger('click') |
| await flushPromises() |
| expect(wrapper.text()).toContain('Enable location access') |
| }) |
|
|
| it('renders the dark variant styling', async () => { |
| Object.defineProperty(navigator, 'geolocation', { |
| value: { getCurrentPosition: (_res, rej) => rej(new Error('denied')) }, |
| configurable: true, |
| }) |
| const wrapper = mount(WeatherWidget, { props: { variant: 'dark' } }) |
| await flushPromises() |
| expect(wrapper.find('div').classes()).toContain('bg-[#162236]') |
| }) |
| }) |
|
|