333 lines
9.5 KiB
TypeScript
333 lines
9.5 KiB
TypeScript
import OpenWeatherMap from 'openweathermap-ts';
|
|
import { describe, expect, it, vi, beforeEach, type Mocked } from 'vitest';
|
|
import { Weather } from '../src/weather.js';
|
|
import type { CurrentResponse, ThreeHourResponse } from '../src/weather.js';
|
|
|
|
// #region mock API responses
|
|
const current: CurrentResponse = {
|
|
"coord": {
|
|
"lon": 7.367,
|
|
"lat": 45.133
|
|
},
|
|
"weather": [
|
|
{
|
|
"id": 501,
|
|
"main": "Rain",
|
|
"description": "moderate rain",
|
|
"icon": "10d"
|
|
}
|
|
],
|
|
"base": "stations",
|
|
"main": {
|
|
"temp": 284.2,
|
|
"feels_like": 282.93,
|
|
"temp_min": 283.06,
|
|
"temp_max": 286.82,
|
|
"pressure": 1021,
|
|
"humidity": 60
|
|
},
|
|
"visibility": 10000,
|
|
"wind": {
|
|
"speed": 4.09,
|
|
"deg": 121
|
|
},
|
|
"clouds": {
|
|
"all": 83
|
|
},
|
|
"dt": 1726660758,
|
|
"sys": {
|
|
"type": 1,
|
|
"id": 6736,
|
|
"country": "IT",
|
|
"sunrise": 1726636384,
|
|
"sunset": 1726680975
|
|
},
|
|
"timezone": 7200,
|
|
"id": 3165523,
|
|
"name": "Province of Turin",
|
|
"cod": 200
|
|
};
|
|
|
|
const threeHour: ThreeHourResponse = {
|
|
"cod": "200",
|
|
"message": 0,
|
|
"cnt": 96,
|
|
"list": [
|
|
{
|
|
"dt": 1661875200,
|
|
"main": {
|
|
"temp": 296.34,
|
|
"temp_min": 296.34,
|
|
"temp_max": 298.24,
|
|
"pressure": 1015,
|
|
"sea_level": 1015,
|
|
"grnd_level": 933,
|
|
"humidity": 50,
|
|
"temp_kf": -1.9
|
|
},
|
|
"weather": [
|
|
{
|
|
"id": 500,
|
|
"main": "Rain",
|
|
"description": "light rain",
|
|
"icon": "10d"
|
|
}
|
|
],
|
|
"clouds": {
|
|
"all": 97
|
|
},
|
|
"wind": {
|
|
"speed": 1.06,
|
|
"deg": 66
|
|
},
|
|
"rain": {
|
|
"3h": 1
|
|
},
|
|
"sys": {
|
|
"pod": "d"
|
|
},
|
|
"dt_txt": "2022-08-30 16:00:00"
|
|
},
|
|
{
|
|
"dt": 1661878800,
|
|
"main": {
|
|
"temp": 296.31,
|
|
"temp_min": 296.2,
|
|
"temp_max": 296.31,
|
|
"pressure": 1015,
|
|
"sea_level": 1015,
|
|
"grnd_level": 932,
|
|
"humidity": 53,
|
|
"temp_kf": 0.11
|
|
},
|
|
"weather": [
|
|
{
|
|
"id": 500,
|
|
"main": "Rain",
|
|
"description": "light rain",
|
|
"icon": "10d"
|
|
}
|
|
],
|
|
"clouds": {
|
|
"all": 95
|
|
},
|
|
"wind": {
|
|
"speed": 1.58,
|
|
"deg": 103
|
|
},
|
|
"rain": {
|
|
"3h": 0.24
|
|
},
|
|
"sys": {
|
|
"pod": "d"
|
|
},
|
|
"dt_txt": "2022-08-30 17:00:00"
|
|
},
|
|
{
|
|
"dt": 1661882400,
|
|
"main": {
|
|
"temp": 294.94,
|
|
"temp_min": 292.84,
|
|
"temp_max": 294.94,
|
|
"pressure": 1015,
|
|
"sea_level": 1015,
|
|
"grnd_level": 931,
|
|
"humidity": 60,
|
|
"temp_kf": 2.1
|
|
},
|
|
"weather": [
|
|
{
|
|
"id": 500,
|
|
"main": "Rain",
|
|
"description": "light rain",
|
|
"icon": "10n"
|
|
}
|
|
],
|
|
"clouds": {
|
|
"all": 93
|
|
},
|
|
"wind": {
|
|
"speed": 1.97,
|
|
"deg": 157
|
|
},
|
|
"rain": {
|
|
"3h": 0.2
|
|
},
|
|
"sys": {
|
|
"pod": "n"
|
|
},
|
|
"dt_txt": "2022-08-30 18:00:00"
|
|
},
|
|
{
|
|
"dt": 1662217200,
|
|
"main": {
|
|
"temp": 294.14,
|
|
"temp_min": 294.14,
|
|
"temp_max": 294.14,
|
|
"pressure": 1014,
|
|
"sea_level": 1014,
|
|
"grnd_level": 931,
|
|
"humidity": 65,
|
|
"temp_kf": 0
|
|
},
|
|
"weather": [
|
|
{
|
|
"id": 804,
|
|
"main": "Clouds",
|
|
"description": "overcast clouds",
|
|
"icon": "04d"
|
|
}
|
|
],
|
|
"clouds": {
|
|
"all": 100
|
|
},
|
|
"wind": {
|
|
"speed": 0.91,
|
|
"deg": 104
|
|
},
|
|
"sys": {
|
|
"pod": "d"
|
|
},
|
|
"dt_txt": "2022-09-03 15:00:00"
|
|
}
|
|
],
|
|
"city": {
|
|
"id": 3163858,
|
|
"name": "Zocca",
|
|
"coord": {
|
|
"lat": 44.34,
|
|
"lon": 10.99
|
|
},
|
|
"country": "IT"
|
|
}
|
|
}
|
|
// #endregion
|
|
|
|
vi.mock('openweathermap-ts', () => {
|
|
return {
|
|
default: {
|
|
default: vi.fn().mockImplementation((_) => {
|
|
return {
|
|
setCityName: vi.fn(() => undefined),
|
|
setCityId: vi.fn(() => undefined),
|
|
setZipCode: vi.fn(() => undefined),
|
|
setGeoCoordinates: vi.fn(() => undefined),
|
|
getCurrentWeatherByCityName: vi.fn(async () => current),
|
|
getCurrentWeatherByCityId: vi.fn(async () => current),
|
|
getCurrentWeatherByZipcode: vi.fn(async () => current),
|
|
getCurrentWeatherByGeoCoordinates: vi.fn(async () => current),
|
|
getThreeHourForecastByCityName: vi.fn(async () => threeHour),
|
|
getThreeHourForecastByCityId: vi.fn(async () => threeHour),
|
|
getThreeHourForecastByZipcode: vi.fn(async () => threeHour),
|
|
getThreeHourForecastByGeoCoordinates: vi.fn(async () => threeHour),
|
|
}
|
|
})
|
|
}
|
|
}
|
|
});
|
|
|
|
let weather: Weather = null;
|
|
|
|
describe.for(
|
|
[
|
|
{
|
|
weatherFactory: () => new Weather({
|
|
key: 'api-key',
|
|
city: {
|
|
cityName: 'Madison',
|
|
state: 'Wisconsin',
|
|
countryCode: 'US'
|
|
}
|
|
}),
|
|
by: 'CityName'
|
|
},
|
|
{
|
|
weatherFactory: () => new Weather({
|
|
key: 'api-key',
|
|
cityid: 1
|
|
}),
|
|
by: 'CityId'
|
|
},
|
|
{
|
|
weatherFactory: () => new Weather({
|
|
key: 'api-key',
|
|
zip: 53702,
|
|
country: 'US'
|
|
}),
|
|
by: 'Zipcode'
|
|
},
|
|
{
|
|
weatherFactory: () => new Weather({
|
|
key: 'api-key',
|
|
coordinates: [0, 0]
|
|
}),
|
|
by: 'GeoCoordinates'
|
|
},
|
|
{
|
|
weatherFactory: () => new Weather({
|
|
key: 'api-key',
|
|
coordinates: '1000 , 1000'
|
|
}),
|
|
by: 'GeoCoordinates'
|
|
}
|
|
])('weather API using city name', ({ weatherFactory, by }) => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
weather = weatherFactory();
|
|
});
|
|
|
|
it('gets current weather', async () => {
|
|
expect(await weather.getCurrentWeather()).to.deep.equal(current);
|
|
expect(OpenWeatherMap.default).toBeCalledWith({
|
|
apiKey: 'api-key'
|
|
});
|
|
const MockedOpenWeather = vi.mocked(OpenWeatherMap.default);
|
|
const openWeather = MockedOpenWeather.mock.results[0].value;
|
|
expect(openWeather[`getCurrentWeatherBy${by}`]).toBeCalledTimes(1);
|
|
});
|
|
|
|
it('gets the 3h forecast', async () => {
|
|
expect(await weather.getThreeHourForecast()).to.deep.equal(threeHour);
|
|
expect(OpenWeatherMap.default).toBeCalledWith({
|
|
apiKey: 'api-key'
|
|
});
|
|
const MockedOpenWeather = vi.mocked(OpenWeatherMap.default);
|
|
const openWeather = MockedOpenWeather.mock.results[0].value;
|
|
expect(openWeather[`getThreeHourForecastBy${by}`]).toBeCalledTimes(1);
|
|
});
|
|
|
|
it('only calls the api once', async () => {
|
|
expect(await weather.getCurrentWeather()).to.deep.equal(current);
|
|
expect(await weather.getCurrentWeather()).to.deep.equal(current);
|
|
expect(await weather.getThreeHourForecast()).to.deep.equal(threeHour);
|
|
expect(await weather.getThreeHourForecast()).to.deep.equal(threeHour);
|
|
expect(OpenWeatherMap.default).toBeCalledWith({
|
|
apiKey: 'api-key'
|
|
});
|
|
const MockedOpenWeather = vi.mocked(OpenWeatherMap.default);
|
|
const openWeather = MockedOpenWeather.mock.results[0].value;
|
|
expect(openWeather[`getCurrentWeatherBy${by}`]).toBeCalledTimes(1);
|
|
expect(openWeather[`getThreeHourForecastBy${by}`]).toBeCalledTimes(1);
|
|
});
|
|
}
|
|
);
|
|
|
|
describe('invalid weather object', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
weather = new Weather({
|
|
key: 'api-key',
|
|
cityid: 1
|
|
});
|
|
weather['locationType'] = null;
|
|
});
|
|
|
|
it('throws an exception when getCurrentWeather is called', async () => {
|
|
await expect(weather.getCurrentWeather()).rejects.toThrow(/location type/);
|
|
});
|
|
|
|
it('throws an exception when getThreeHourForecast is called', async () => {
|
|
await expect(weather.getThreeHourForecast()).rejects.toThrow(/location type/);
|
|
});
|
|
});
|