diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml
index 1c4c8fdb..f4f222ac 100644
--- a/.github/workflows/run-unit-tests.yml
+++ b/.github/workflows/run-unit-tests.yml
@@ -13,6 +13,7 @@ jobs:
NEXT_PUBLIC_SUPABASE_URL: http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
SUPABASE_SERVICE_ROLE_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
+ GOOGLE_MAPS_API_KEY: dummy_key
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
diff --git a/src/__tests__/unit/app/progress/__snapshots__/page.snapshot.tsx.snap b/src/__tests__/unit/app/progress/__snapshots__/page.snapshot.tsx.snap
index 26156210..2d194cbf 100644
--- a/src/__tests__/unit/app/progress/__snapshots__/page.snapshot.tsx.snap
+++ b/src/__tests__/unit/app/progress/__snapshots__/page.snapshot.tsx.snap
@@ -94,11 +94,12 @@ exports[`Progress renders progress page unchanged 1`] = `
Not registered to vote yet?
Register now
- and earn a badge!
+
+ and earn a badge!
@@ -355,11 +356,12 @@ exports[`Progress renders progress page unchanged 1`] = `
Not registered to vote yet?
Register now
- and earn a badge!
+
+ and earn a badge!
diff --git a/src/__tests__/unit/components/form-components/input-group.test.tsx b/src/__tests__/unit/components/form-components/input-group.test.tsx
index 7c975004..19a3ff54 100644
--- a/src/__tests__/unit/components/form-components/input-group.test.tsx
+++ b/src/__tests__/unit/components/form-components/input-group.test.tsx
@@ -27,7 +27,7 @@ describe('InputGroup', () => {
expect(input.id).toBe(field.id);
});
- it(`renders an input element whose aria-describedby attribute is set to the id
+ it(`renders an input element whose aria-describedby attribute includes the id
of the Messages component it renders.`, () => {
const invalidEmailMessage = 'Please enter a valid email address.';
@@ -52,7 +52,7 @@ describe('InputGroup', () => {
const input = screen.getByRole('textbox') as HTMLInputElement;
const messagesId = `${email.id}-messages`;
- expect(input.getAttribute('aria-describedby')).toBe(messagesId);
+ expect(input.getAttribute('aria-describedby')).toContain(messagesId);
const messages = document.getElementById(messagesId);
expect(messages).toBeTruthy();
diff --git a/src/__tests__/unit/components/form-components/phone-input-group.test.tsx b/src/__tests__/unit/components/form-components/phone-input-group.test.tsx
index 0b31764e..128479aa 100644
--- a/src/__tests__/unit/components/form-components/phone-input-group.test.tsx
+++ b/src/__tests__/unit/components/form-components/phone-input-group.test.tsx
@@ -26,7 +26,7 @@ describe('PhoneInputGroup', () => {
expect(input.id).toBe(phoneNumber.id);
});
- it(`renders an input element whose aria-describedby attribute is set to the id
+ it(`renders an input element whose aria-describedby attribute includes the id
of the Messages component it renders.`, () => {
const invalidMessage = 'Please enter a valid phone number.';
@@ -51,7 +51,7 @@ describe('PhoneInputGroup', () => {
const input = screen.getByRole('textbox') as HTMLInputElement;
const messagesId = `${phoneNumber.id}-messages`;
- expect(input.getAttribute('aria-describedby')).toBe(messagesId);
+ expect(input.getAttribute('aria-describedby')).toContain(messagesId);
const messages = document.getElementById(messagesId);
expect(messages).toBeTruthy();
diff --git a/src/__tests__/unit/components/form-components/select/select.test.tsx b/src/__tests__/unit/components/form-components/select/select.test.tsx
index 0c1e188c..26521ee2 100644
--- a/src/__tests__/unit/components/form-components/select/select.test.tsx
+++ b/src/__tests__/unit/components/form-components/select/select.test.tsx
@@ -514,40 +514,6 @@ describe('Select', () => {
expect(outerContainer.classList).toContain(className);
});
- it(`displays any messages associated with the field if the field has been
- blurred.`, async () => {
- const invalidMessage = 'Please select an option.';
-
- const requiredField = new Field({
- name: 'requiredField',
- defaultValue: '',
- validators: [
- StringValidators.required({
- invalidMessage,
- }),
- ],
- });
-
- render(
- ,
- );
-
- expect(screen.getByText(invalidMessage).classList).toContain(
- 'hidden_message',
- );
-
- const combobox = screen.getByRole('combobox');
- await user.click(combobox);
- expect(screen.getByText(invalidMessage).classList).toContain(
- 'hidden_message',
- );
-
- await user.click(document.body);
- expect(screen.getByText(invalidMessage).classList).not.toContain(
- 'hidden_message',
- );
- });
-
it(`renders any messages associated with the field if the field has been
modified.`, async () => {
const validMessage = 'Thank you for selecting an option.';
diff --git a/src/__tests__/unit/components/utils/loading-wheel/__snapshots__/loading-wheel.snapshot.tsx.snap b/src/__tests__/unit/components/utils/loading-wheel/__snapshots__/loading-wheel.snapshot.tsx.snap
index d76732d0..00ecca65 100644
--- a/src/__tests__/unit/components/utils/loading-wheel/__snapshots__/loading-wheel.snapshot.tsx.snap
+++ b/src/__tests__/unit/components/utils/loading-wheel/__snapshots__/loading-wheel.snapshot.tsx.snap
@@ -13,8 +13,8 @@ exports[`LoadingWheel renders the Spinner unchanged. 1`] = `
class="spinner"
data-nimg="1"
decoding="async"
+ fetchpriority="high"
height="82"
- loading="lazy"
src="/_next/image?url=%2Fstatic%2Fimages%2Fcomponents%2Fspinner%2Fspinner.png&w=256&q=75"
srcset="/_next/image?url=%2Fstatic%2Fimages%2Fcomponents%2Fspinner%2Fspinner.png&w=96&q=75 1x, /_next/image?url=%2Fstatic%2Fimages%2Fcomponents%2Fspinner%2Fspinner.png&w=256&q=75 2x"
style="color: transparent;"
diff --git a/src/__tests__/unit/services/server/validate-addresses/validate-addresses-with-google-maps/validate-addresses-with-google-maps.test.ts b/src/__tests__/unit/services/server/validate-addresses/validate-addresses-with-google-maps/validate-addresses-with-google-maps.test.ts
new file mode 100644
index 00000000..c36580bd
--- /dev/null
+++ b/src/__tests__/unit/services/server/validate-addresses/validate-addresses-with-google-maps/validate-addresses-with-google-maps.test.ts
@@ -0,0 +1,113 @@
+import { validateAddressesWithGoogleMaps } from '@/services/server/validate-addresses/validate-addresses-with-google-maps';
+import { Builder } from 'builder-pattern';
+import type { ProcessableResponse } from '@/services/server/validate-addresses/validate-addresses-with-google-maps/types/processable-response';
+import type { Address } from '@/model/types/addresses/address';
+
+describe('validateAddressesWithGoogleMaps', () => {
+ it('Returns an empty array if all addresses it receives are valid.', async () => {
+ const address: Address = {
+ streetLine1: '1600 Amphitheatre Pkwy',
+ city: 'Mountain View',
+ state: 'CA',
+ zip: '94043',
+ };
+
+ jest.spyOn(globalThis, 'fetch').mockImplementation(() => {
+ console.log('spy called');
+
+ const response = Builder()
+ .ok(true)
+ .json(() => {
+ const processableResponseBody: ProcessableResponse = {
+ result: {
+ verdict: {
+ hasUnconfirmedComponents: false,
+ },
+ address: {
+ postalAddress: {
+ postalCode: address.zip,
+ administrativeArea: address.state,
+ locality: address.city,
+ addressLines: [address.streetLine1],
+ },
+ addressComponents: [
+ {
+ componentName: {
+ text: address.streetLine1.slice(0, 4),
+ },
+ componentType: 'street_number',
+ confirmationLevel: 'CONFIRMED',
+ },
+ {
+ componentName: {
+ text: address.streetLine1.slice(5),
+ },
+ componentType: 'route',
+ confirmationLevel: 'CONFIRMED',
+ },
+ {
+ componentName: {
+ text: address.city,
+ },
+ componentType: 'locality',
+ confirmationLevel: 'CONFIRMED',
+ },
+ {
+ componentName: {
+ text: address.state,
+ },
+ componentType: 'administrative_area_level_1',
+ confirmationLevel: 'CONFIRMED',
+ },
+ {
+ componentName: {
+ text: address.zip,
+ },
+ componentType: 'postal_code',
+ confirmationLevel: 'CONFIRMED',
+ },
+ ],
+ },
+ },
+ };
+
+ return Promise.resolve(processableResponseBody);
+ })
+ .build();
+
+ return Promise.resolve(response);
+ });
+
+ const result = await validateAddressesWithGoogleMaps({
+ homeAddress: address,
+ mailingAddress: address,
+ previousAddress: address,
+ });
+
+ expect(result).toStrictEqual([]);
+ });
+
+ // it(`throws a ServerError if the response received from the Google maps API is
+ // not ok.`, () => {});
+
+ // it(`throws a ServerError if the response received from the Google maps API
+ // cannot be processed due to missing or invalid properties.`, () => {});
+
+ // it(`returns an array containing an UnconfirmedComponentsError if the address
+ // has components that could not be confirmed.`, () => {});
+
+ // it(`returns an array containing a ReviewRecommendedAddressError if the
+ // address did not contain unconfirmed components but a more accurate address
+ // was found.`, () => {});
+
+ // it(`returns an array containing a MissingSubpremiseError if a missing
+ // subpremise was detected.`, () => {});
+
+ // it(`returns an array containing multiple error types when multiple problems
+ // are detected.`, () => {});
+
+ /*
+ The following tests can be uncommented to test against the actual
+ Google Maps API.
+ */
+});
diff --git a/src/__tests__/unit/utils/shared/collapse-whitespace.test.ts b/src/__tests__/unit/utils/shared/collapse-whitespace.test.ts
new file mode 100644
index 00000000..886bab16
--- /dev/null
+++ b/src/__tests__/unit/utils/shared/collapse-whitespace.test.ts
@@ -0,0 +1,17 @@
+import { collapseWhitespace } from '@/utils/shared/collapse-whitespace';
+
+describe('collapseWhitespace', () => {
+ const whitespace = ' \t\n\r ';
+ const collapsed = 'The quick brown fox jumps over the lazy dog';
+
+ test(`It collapses groups of whitespace characters within a string into
+ single space characters.`, () => {
+ const expanded = collapsed.split(' ').join(whitespace);
+ expect(collapseWhitespace(expanded)).toBe(collapsed);
+ });
+
+ test('It removes whitespace at the start and end of a string.', () => {
+ const testString = whitespace + collapsed + whitespace;
+ expect(collapseWhitespace(testString)).toBe(collapsed);
+ });
+});