Skip to content

Commit

Permalink
Fetch constants from api instead of having values hardcoded (#2787)
Browse files Browse the repository at this point in the history
* Fetch constants from api instead of having values hardcoded

* Prepare code for final version

* Add dedicated tests

* Correct tests

* Correct two words path by using an underscore

* Ajust final paths for new types (Architectures & OperatingSystems)

---------

Co-authored-by: Martijn Verburg <martijnverburg@gmail.com>
  • Loading branch information
xavierfacq and karianna committed Apr 30, 2024
1 parent 305e6dd commit b69b24b
Show file tree
Hide file tree
Showing 14 changed files with 319 additions and 144 deletions.
11 changes: 10 additions & 1 deletion src/__fixtures__/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
NewsResponse,
ReleaseAsset,
TemurinReleases,
OperatingSystem,
Architecture
} from '../hooks';

export const createRandomTemurinRelease = (installer): ReleaseAsset => ({
Expand Down Expand Up @@ -232,7 +234,6 @@ export const createMockReleaseNotesAPI = (number): ReleaseNoteAPIResponse => ({
}))
});


export const createMockTemurinFeatureReleaseAPI = (installer): MockTemurinFeatureReleaseAPI => ({
id: 'id_mock',
download_count: 0,
Expand Down Expand Up @@ -377,3 +378,11 @@ export const createMockAdoptiumContributorsApi = (): ContributorApiResponse => (
site_admin: false
}
);

export const mockOsesAPI = (): OperatingSystem[] => (
[{name: "mock_macos"}, {name: "mock_linux"}, {name: "mock_windows"}]
);

export const mockArchesAPI = (): Architecture[] => (
[{name: "mock_aarch64"}, {name: "mock_ppc64"}, {name: "mock_x64"}
]);
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { act, render, fireEvent, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest'
import { createRandomTemurinReleases } from '../../../__fixtures__/hooks';
import { createRandomTemurinReleases, mockOsesAPI, mockArchesAPI } from '../../../__fixtures__/hooks';
import DownloadDropdowns from '..';
import queryString from 'query-string';

Expand All @@ -11,6 +11,17 @@ const Table = () => {
);
};

vi.mock('../../../hooks/fetchConstants', () => {
return {
fetchOses: () => {
return mockOsesAPI();
},
fetchArches: () => {
return mockArchesAPI();
}
};
});

vi.mock('../../VendorSelector', () => {
return {
default: () => <div>vendor-selector</div>,
Expand All @@ -19,8 +30,6 @@ vi.mock('../../VendorSelector', () => {

vi.mock('../../../util/defaults', () => {
return {
oses: ['mock_os'],
arches: ['mock_arch'],
packageTypes: ['mock_pkg'],
defaultPackageType: 'mock_pkg',
defaultArchitecture: 'mock_arch',
Expand Down Expand Up @@ -71,14 +80,14 @@ describe('DownloadDropdowns component', () => {
// Simulate a user using dropdowns
select = getByTestId('os-filter');
await act(async () => {
fireEvent.change(select, { target: { value: 'mock_os' } });
fireEvent.change(select, { target: { value: 'mock_linux' } });
});

expect(updater).toHaveBeenCalledTimes(2);

select = getByTestId('arch-filter');
await act(async () => {
fireEvent.change(select, { target: { value: 'mock_arch' } });
fireEvent.change(select, { target: { value: 'mock_x64' } });
});

expect(updater).toHaveBeenCalledTimes(3);
Expand All @@ -100,7 +109,7 @@ describe('DownloadDropdowns component', () => {
});

it('renders correctly - use URL param os=mock_os', async () => {
queryString.parse = vi.fn().mockReturnValue({'os': "mock_os"});
queryString.parse = vi.fn().mockReturnValue({'os': "mock_linux"});

let getByRole;
await act(async () => {
Expand All @@ -113,11 +122,11 @@ describe('DownloadDropdowns component', () => {
));
});

expect(getByRole('option', { name: 'Mock_os' }).selected).toBeTruthy()
expect(getByRole('option', { name: 'Mock_linux' }).selected).toBeTruthy()
});

it('renders correctly - use URL param arch=mock_arch', async () => {
queryString.parse = vi.fn().mockReturnValue({'arch': "mock_arch"});
queryString.parse = vi.fn().mockReturnValue({'arch': "mock_x64"});

let getByRole;
await act(async () => {
Expand All @@ -130,7 +139,7 @@ describe('DownloadDropdowns component', () => {
));
});

expect(getByRole('option', { name: 'mock_arch' }).selected).toBeTruthy()
expect(getByRole('option', { name: 'mock_x64' }).selected).toBeTruthy()
});

it('renders correctly - use URL param package=mock_pkg', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,19 @@ exports[`DownloadDropdowns component > renders correctly - marketplace 1`] = `
Text
</option>
<option
value="mock_os"
value="mock_linux"
>
Mock_os
Mock_linux
</option>
<option
value="mock_macos"
>
Mock_macos
</option>
<option
value="mock_windows"
>
Mock_windows
</option>
</select>
</div>
Expand All @@ -56,9 +66,19 @@ exports[`DownloadDropdowns component > renders correctly - marketplace 1`] = `
Text
</option>
<option
value="mock_arch"
value="mock_aarch64"
>
mock_aarch64
</option>
<option
value="mock_ppc64"
>
mock_ppc64
</option>
<option
value="mock_x64"
>
mock_arch
mock_x64
</option>
</select>
</div>
Expand Down Expand Up @@ -144,9 +164,19 @@ exports[`DownloadDropdowns component > renders correctly 1`] = `
Text
</option>
<option
value="mock_os"
value="mock_linux"
>
Mock_os
Mock_linux
</option>
<option
value="mock_macos"
>
Mock_macos
</option>
<option
value="mock_windows"
>
Mock_windows
</option>
</select>
</div>
Expand All @@ -171,9 +201,19 @@ exports[`DownloadDropdowns component > renders correctly 1`] = `
Text
</option>
<option
value="mock_arch"
value="mock_aarch64"
>
mock_aarch64
</option>
<option
value="mock_ppc64"
>
mock_ppc64
</option>
<option
value="mock_x64"
>
mock_arch
mock_x64
</option>
</select>
</div>
Expand Down
15 changes: 8 additions & 7 deletions src/components/DownloadDropdowns/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import VendorSelector from '../VendorSelector'
import { detectOS, UserOS } from '../../util/detectOS';
import { setURLParam } from '../../util/setURLParam';
import { capitalize } from '../../util/capitalize';
import { oses, arches, packageTypes, defaultArchitecture, defaultPackageType} from '../../util/defaults';
import { packageTypes, defaultArchitecture, defaultPackageType} from '../../util/defaults';
import { fetchOses, fetchArches} from '../../hooks/fetchConstants';

const DownloadDropdowns = ({updaterAction, marketplace, Table}) => {

Expand Down Expand Up @@ -42,7 +43,7 @@ const DownloadDropdowns = ({updaterAction, marketplace, Table}) => {
const osParam = queryStringParams.os;
if (osParam) {
let sop = osParam.toString().toLowerCase();
if(oses.findIndex(os => os.toLowerCase() === sop) >= 0)
if(fetchOses(true).findIndex(os => os.name.toLowerCase() === sop) >= 0)
defaultSelectedOS = sop;
}

Expand All @@ -51,7 +52,7 @@ const DownloadDropdowns = ({updaterAction, marketplace, Table}) => {
const archParam = queryStringParams.arch;
if (archParam) {
let sap = archParam.toString().toLowerCase();
if(arches.findIndex(a => a.toLowerCase() === sap) >= 0)
if(fetchArches(true).findIndex(a => a.name.toLowerCase() === sap) >= 0)
defaultSelectedArch = sap;
}

Expand Down Expand Up @@ -182,9 +183,9 @@ const DownloadDropdowns = ({updaterAction, marketplace, Table}) => {
<label className="px-2 fw-bold" htmlFor="os"><Trans>Operating System</Trans></label>
<select id="os-filter" aria-label="OS Filter" data-testid="os-filter" onChange={(e) => setOS(e.target.value)} value={os} className="form-select form-select-sm">
<option key="any" value="any"><Trans>Any</Trans></option>
{oses.sort((os1, os2) => os1.localeCompare(os2)).map(
{fetchOses(true).sort((os1, os2) => os1.name.localeCompare(os2.name)).map(
(os, i): string | JSX.Element => os && (
<option key={`os-${i}`} value={os.toLowerCase()}>{capitalize(os)}</option>
<option key={`os-${i}`} value={os.name.toLowerCase()}>{capitalize(os.name)}</option>
)
)}
</select>
Expand All @@ -193,9 +194,9 @@ const DownloadDropdowns = ({updaterAction, marketplace, Table}) => {
<label className="px-2 fw-bold" htmlFor="arch"><Trans>Architecture</Trans></label>
<select id="arch-filter" aria-label="Architecture Filter" data-testid="arch-filter" onChange={(e) => setArch(e.target.value)} value={arch} className="form-select form-select-sm">
<option key="any" value="any"><Trans>Any</Trans></option>
{arches.sort((arch1, arch2) => arch1.localeCompare(arch2)).map(
{fetchArches(true).sort((arch1, arch2) => arch1.name.localeCompare(arch2.name)).map(
(arch, i): string | JSX.Element => arch && (
<option key={`arch-${i}`} value={arch.toLowerCase()}>{arch}</option>
<option key={`arch-${i}`} value={arch.name.toLowerCase()}>{arch.name}</option>
)
)}
</select>
Expand Down
37 changes: 37 additions & 0 deletions src/hooks/__tests__/__snapshots__/fetchConstants.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`fetchArches > URL is set correctly 1`] = `
[
{
"name": [
{
"name": "mock_aarch64",
},
{
"name": "mock_ppc64",
},
{
"name": "mock_x64",
},
],
},
]
`;

exports[`fetchOses > URL is set correctly 1`] = `
[
{
"name": [
{
"name": "mock_macos",
},
{
"name": "mock_linux",
},
{
"name": "mock_windows",
},
],
},
]
`;
79 changes: 79 additions & 0 deletions src/hooks/__tests__/fetchConstants.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { renderHook, waitFor } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import { fetchArches, fetchOses } from '../fetchConstants';
import { mockArchesAPI, mockOsesAPI } from '../../__fixtures__/hooks';
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter';

const mock = new MockAdapter(axios);

afterEach(() => {
vi.clearAllMocks();
mock.reset();
});

afterAll(() => {
mock.restore();
});

describe('fetchArches', () => {
it('URL is set correctly', async () => {
const mockResponse = [mockArchesAPI()];

mock.onGet().reply(200, mockResponse);
let spy = vi.spyOn(axios, "get");

const { result } = renderHook(() => fetchArches(true));

await waitFor(() => {
expect(result.current.length).toBeGreaterThan(0)
}, { interval: 1 });
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(
"https://api.adoptium.net/v3/types/architectures"
);
expect(result.current).toMatchSnapshot()
})

it('Result is empty on error', async() => {
mock.onGet().reply(500);
let spy = vi.spyOn(axios, "get");

const { result } = renderHook(() => fetchArches(true));
await waitFor(() => {
expect(result.current.length).toBe(0)
}, { interval: 1 });
expect(spy).toHaveBeenCalledTimes(1)
})
});

describe('fetchOses', () => {
it('URL is set correctly', async () => {
const mockResponse = [mockOsesAPI()];

mock.onGet().reply(200, mockResponse);
let spy = vi.spyOn(axios, "get");

const { result } = renderHook(() => fetchOses(true));

await waitFor(() => {
expect(result.current.length).toBeGreaterThan(0)
}, { interval: 1 });
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(
"https://api.adoptium.net/v3/types/operating_systems"
);
expect(result.current).toMatchSnapshot()
})

it('Result is empty on error', async() => {
mock.onGet().reply(500);
let spy = vi.spyOn(axios, "get");

const { result } = renderHook(() => fetchOses(true));
await waitFor(() => {
expect(result.current.length).toBe(0)
}, { interval: 1 });
expect(spy).toHaveBeenCalledTimes(1)
})
});
Loading

0 comments on commit b69b24b

Please sign in to comment.