From 6cabfb2a38a8ce56959e5210d065e32817bfc8ad Mon Sep 17 00:00:00 2001 From: Parker Date: Sun, 11 Feb 2024 10:34:24 -0700 Subject: [PATCH 1/3] Handle geolocation error, handle timezone error, tests --- playwright.config.js | 3 +++ .../PreComponents/FormLocate.svelte | 9 +++++-- .../PreComponents/PreInputForm.svelte | 2 +- src/routes/postGetWeather.js | 12 ++++++--- src/routes/preGetWeather.js | 11 +++++++- tests/geolocation.test.js | 22 +++++++++++++++ tests/test.js | 27 ------------------- tests/timezone.test.js | 0 8 files changed, 52 insertions(+), 34 deletions(-) create mode 100644 tests/geolocation.test.js create mode 100644 tests/timezone.test.js diff --git a/playwright.config.js b/playwright.config.js index a43c8c0..33102de 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -4,6 +4,9 @@ const config = { command: 'npm run build && npm run preview', port: 4173 }, + use: { + permissions: ['geolocation'] + }, testDir: 'tests', testMatch: /(.+\.)?(test|spec)\.[jt]s/ }; diff --git a/src/lib/components/PreComponents/FormLocate.svelte b/src/lib/components/PreComponents/FormLocate.svelte index 6985df9..652dc65 100644 --- a/src/lib/components/PreComponents/FormLocate.svelte +++ b/src/lib/components/PreComponents/FormLocate.svelte @@ -50,7 +50,11 @@ console.log(position.coords); $preFormInput.latlon = `${position.coords.latitude},${position.coords.longitude}`; }; - await navigator.geolocation.getCurrentPosition(success, error, options); + try { + await navigator.geolocation.getCurrentPosition(success, error, options); + } catch (error) { + console.error(error); + } }; @@ -81,7 +85,8 @@ {#if locateError.error} - {#if locateError.code === 1}{$_('pre_submit.locate_error')} {/if}({locateError.message}) + {#if locateError.code === 1}{$_('pre_submit.locate_error')} + {/if}({locateError.message}) {/if} diff --git a/src/lib/components/PreComponents/PreInputForm.svelte b/src/lib/components/PreComponents/PreInputForm.svelte index 397dd55..bbb9095 100644 --- a/src/lib/components/PreComponents/PreInputForm.svelte +++ b/src/lib/components/PreComponents/PreInputForm.svelte @@ -52,7 +52,7 @@ if (result.type === 'failure') { // render error text if ( - result.data.type === 'Timezone Offset Error' || + result.data.type === 'timezoneOffsetError' || result.data.type === 'GetWeatherForStartAndEnd error' ) { $preErrorText = result.data.message; diff --git a/src/routes/postGetWeather.js b/src/routes/postGetWeather.js index 24a862d..6b51505 100644 --- a/src/routes/postGetWeather.js +++ b/src/routes/postGetWeather.js @@ -75,13 +75,19 @@ export default async function postGetWeather({ fetch, request, cookies }) { console.log('dayjsTimes', dayjsTimes); // ---- Get unixtime from timezone ---- - const tz = find(postWeather.location.lat, postWeather.location.lon); - console.log('timezone', tz); + let tz; + try { + tz = find(postWeather.location.lat, postWeather.location.lon); + } catch (error) { + return fail(400, { + type: 'timezoneOffsetError', + message: 'Error getting time zone for checklist coordinates' + }); + } dayjsTimes.start.unixTime = dayjsTimes.start.localTime.tz(tz, true).unix(); if (dayjsTimes.end.localTime) { dayjsTimes.end.unixTime = dayjsTimes.end.localTime.tz(tz, true).unix(); } - console.log('unixTimes included', dayjsTimes); // ---- Query weather ---- postWeather.weatherResults = await getWeatherForStartAndEnd(postWeather, dayjsTimes, fetch); diff --git a/src/routes/preGetWeather.js b/src/routes/preGetWeather.js index 500be13..c081b59 100644 --- a/src/routes/preGetWeather.js +++ b/src/routes/preGetWeather.js @@ -70,7 +70,16 @@ export default async function preGetWeather({ fetch, request, cookies }) { dayjsTimes.end.localTime = dayjs(dayjsTimes.start.localTime).add(duration, 'minute'); // ---- Get unixtime from timezone ---- - const tz = find(preWeather.location.lat, preWeather.location.lon); + let tz; + try { + tz = find(preWeather.location.lat, preWeather.location.lon); + } catch (error) { + return fail(400, { + ...errorObj, + type: 'timezoneOffsetError', + message: 'Error getting time zone for given coordinates' + }); + } dayjsTimes.start.unixTime = dayjsTimes.start.localTime.tz(tz, true).unix(); if (dayjsTimes.end.localTime) { dayjsTimes.end.unixTime = dayjsTimes.end.localTime.tz(tz, true).unix(); diff --git a/tests/geolocation.test.js b/tests/geolocation.test.js new file mode 100644 index 0000000..1a6a6ee --- /dev/null +++ b/tests/geolocation.test.js @@ -0,0 +1,22 @@ +import { expect, test } from '@playwright/test'; + +test('Error is displayed when user denies geolocation', async ({ page }) => { + await page.goto('/'); + await page.getByRole('button', { name: 'Pre-Submit' }).click(); + await page.getByRole('button', { name: 'Locate' }).click(); + await expect(page.getByText('Allow location access to use')).toBeVisible(); +}); + +test.describe('When Geolocation is granted', () => { + test.use({ + geolocation: { longitude: 41.890221, latitude: 12.492348 }, + permissions: ['geolocation'] + }); + + test('Locate button works and fills out the input', async ({ page }) => { + await page.goto('/'); + await page.getByRole('button', { name: 'Pre-Submit' }).click(); + await page.getByRole('button', { name: 'Locate' }).click(); + await expect(page.getByLabel('Location (Latitude, Longitude)')).not.toBeEmpty(); + }); +}); diff --git a/tests/test.js b/tests/test.js index 3b8d18f..af7e7ca 100644 --- a/tests/test.js +++ b/tests/test.js @@ -10,30 +10,3 @@ test('index page has nav bar', async ({ page }) => { await expect(page.getByRole('button', { name: 'Submitted' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Pre-Submit' })).toBeVisible(); }); - -test.beforeEach(async ({ context }) => { - await context.route('https://api.ebird.org/v2/**', async (route) => { - const json = { - message: { checklist: ['info'] } - }; - await route.fulfill({ json }); - }); - await context.route( - 'https://api.openweathermap.org/data/3.0/onecall/timemachine**', - async (route) => { - const json = { - weather: { results: 'hot' } - }; - await route.fulfill({ json }); - } - ); -}); - -test('makes mock API request', async ({ page }) => { - await page.goto('/'); - await page.getByRole('button', { name: 'Submitted' }).click(); - await page.getByLabel('Checklist ID:').click(); - await page.getByLabel('Checklist ID:').fill('https://ebird.org/checklist/S142104802'); - await page.getByRole('button', { name: 'Get Weather' }).click(); - await expect(page.getByTestId('weatherResultsPane').toContainText('fail')); -}); diff --git a/tests/timezone.test.js b/tests/timezone.test.js new file mode 100644 index 0000000..e69de29 From 6ffcaadd1a7223c48d07f51fb71f744b55929ca7 Mon Sep 17 00:00:00 2001 From: Parker Date: Sun, 11 Feb 2024 10:49:26 -0700 Subject: [PATCH 2/3] Validate latlons to be -90 to 90, -180 to 180 --- src/lib/services/validation.js | 11 ++++++++++- src/lib/services/validation.test.js | 15 +++++++++++++++ tests/timezone.test.js | 8 ++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/lib/services/validation.test.js diff --git a/src/lib/services/validation.js b/src/lib/services/validation.js index 956c0ce..4a452ed 100644 --- a/src/lib/services/validation.js +++ b/src/lib/services/validation.js @@ -1,6 +1,15 @@ +import { parseLatlon } from './parseLatlon'; + export const validateLatlon = (latlon) => { const latlonRegex = /\s*-?\d+\.\d+,\s*-?\d+\.\d+\s*/; - return latlon.match(latlonRegex) ? true : false; + const textFormatCheck = latlon.match(latlonRegex) ? true : false; + const { lat, lon } = parseLatlon(latlon); + const latNum = parseInt(lat); + const lonNum = parseInt(lon); + const latNumCheck = latNum >= -90 && latNum <= 90; + const lonNumCheck = lonNum >= -180 && lonNum <= 180; + + return textFormatCheck && latNumCheck && lonNumCheck; }; export const validateDate = (date) => { diff --git a/src/lib/services/validation.test.js b/src/lib/services/validation.test.js new file mode 100644 index 0000000..fdc3371 --- /dev/null +++ b/src/lib/services/validation.test.js @@ -0,0 +1,15 @@ +import { describe, expect, test } from 'vitest'; +import { validateLatlon } from './validation'; + +describe('Validate Lat Lon', () => { + test('validates valid lat lon', () => { + expect(validateLatlon('33.33, -22.22')).toBe(true); + expect(validateLatlon('33.0, -82.2')).toBe(true); + }); + test('Rejects invalid latlon', () => { + expect(validateLatlon('-91.00, 111.11')).toBe(false); + expect(validateLatlon('91.00, 111.11')).toBe(false); + expect(validateLatlon('44.4, -181.0')).toBe(false); + expect(validateLatlon('44.4, 181.0')).toBe(false); + }); +}); diff --git a/tests/timezone.test.js b/tests/timezone.test.js index e69de29..865dc88 100644 --- a/tests/timezone.test.js +++ b/tests/timezone.test.js @@ -0,0 +1,8 @@ +import { expect, test } from '@playwright/test'; + +// test('Error is displayed when user denies geolocation', async ({ page }) => { +// await page.goto('/'); +// await page.getByRole('button', { name: 'Pre-Submit' }).click(); +// await page.getByRole('button', { name: 'Locate' }).click(); +// await expect(page.getByText('Allow location access to use')).toBeVisible(); +// }); From 122ee0b64d610c234a8015cf1957dd896b41dc7e Mon Sep 17 00:00:00 2001 From: Parker Date: Sun, 11 Feb 2024 11:07:07 -0700 Subject: [PATCH 3/3] Added a few tests for geo-tz for problem GPS coords --- src/lib/services/timezone.test.js | 23 +++++++++++++++++++++++ tests/timezone.test.js | 8 -------- 2 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 src/lib/services/timezone.test.js delete mode 100644 tests/timezone.test.js diff --git a/src/lib/services/timezone.test.js b/src/lib/services/timezone.test.js new file mode 100644 index 0000000..e137360 --- /dev/null +++ b/src/lib/services/timezone.test.js @@ -0,0 +1,23 @@ +import { test, expect } from 'vitest'; +import { find } from 'geo-tz'; + +test('Timezone is able to be found with some edge case GPS coordinates', () => { + // threw error in RainCrow with geo-tz 8.0.1 + expect(find(41.796014, -87.576506)).toStrictEqual(['America/Chicago']); + // threw error in RainCrow with geo-tz 8.0.1 + expect(find(24.597694, -81.583389)).toStrictEqual(['America/New_York']); + // threw error in RainCrow with geo-tz 8.0.1 + expect(find(24.566016, -81.673234)).toStrictEqual(['America/New_York']); + // gave problems on github issue apparently + expect(find(-24.244125928804735, -53.8226425697034)).toStrictEqual(['America/Sao_Paulo']); + // gave problems on github issue apparently + expect(find(34.05861, -118.3928)).toStrictEqual(['America/Los_Angeles']); + // Edge of Reservation Land in NE AZ (Rez) + expect(find(35.1578900375291, -111.24227457293293)).toStrictEqual(['America/Denver']); + // Edge of Reservation Land in NE AZ (Non-rez) + expect(find(35.15698602683568, -111.24796115826194)).toStrictEqual(['America/Phoenix']); + // Middle of the Atlantic Ocean + expect(find(43.298646833300026, -38.425597455498185)).toStrictEqual(['Etc/GMT+3']); + // CA/AZ border + expect(find(33.608429524254916, -114.53309086102836)).toStrictEqual(['America/Los_Angeles']); +}); diff --git a/tests/timezone.test.js b/tests/timezone.test.js deleted file mode 100644 index 865dc88..0000000 --- a/tests/timezone.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { expect, test } from '@playwright/test'; - -// test('Error is displayed when user denies geolocation', async ({ page }) => { -// await page.goto('/'); -// await page.getByRole('button', { name: 'Pre-Submit' }).click(); -// await page.getByRole('button', { name: 'Locate' }).click(); -// await expect(page.getByText('Allow location access to use')).toBeVisible(); -// });