Skip to content

Commit 431ff5d

Browse files
committed
refactor: improved typing, added more tests
1 parent 4a9828f commit 431ff5d

File tree

13 files changed

+622
-529
lines changed

13 files changed

+622
-529
lines changed

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
"vanilla-cookieconsent": "^3.1.0"
4646
},
4747
"devDependencies": {
48-
"@babel/core": "^7.26.7",
49-
"@babel/preset-env": "^7.26.7",
48+
"@babel/core": "^7.26.8",
49+
"@babel/preset-env": "^7.26.8",
5050
"@commitlint/cli": "^19.7.1",
5151
"@commitlint/config-conventional": "^19.7.1",
5252
"@types/bootstrap": "^5.2.10",
@@ -64,10 +64,10 @@
6464
"prettier": "^3.4.2",
6565
"pretty-quick": "^4.0.0",
6666
"rollup-plugin-sbom": "^1.1.1",
67-
"sass": "^1.83.4",
67+
"sass": "^1.84.0",
6868
"sigmund": "^1.0.1",
6969
"typescript": "^5.7.3",
70-
"vite": "^6.0.11",
70+
"vite": "^6.1.0",
7171
"vite-plugin-compression": "^0.5.1",
7272
"vite-plugin-html": "^3.2.2",
7373
"vite-plugin-minify": "^2.1.0",

phpmyfaq/admin/assets/src/api/configuration.test.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -313,17 +313,4 @@ describe('saveConfiguration', () => {
313313
body: formData,
314314
});
315315
});
316-
317-
it('should throw an error if the network response is not ok', async () => {
318-
global.fetch = vi.fn(() =>
319-
Promise.resolve({
320-
success: false,
321-
} as unknown as Response)
322-
);
323-
324-
const formData = new FormData();
325-
formData.append('key', 'value');
326-
327-
await expect(saveConfiguration(formData)).rejects.toThrow('Network response was not ok.');
328-
});
329316
});

phpmyfaq/admin/assets/src/api/configuration.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,7 @@ export const saveConfiguration = async (data: FormData): Promise<void> => {
166166
body: data,
167167
})) as unknown as Response;
168168

169-
if (response.success) {
170-
return await response.json();
171-
} else {
172-
throw new Error('Network response was not ok.');
173-
}
169+
return await response.json();
174170
} catch (error) {
175171
throw error;
176172
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { describe, it, expect, vi } from 'vitest';
2+
import { fetchByLanguage, postStopWord, removeStopWord } from './stop-words';
3+
4+
global.fetch = vi.fn();
5+
6+
describe('Stop Words API', () => {
7+
afterEach(() => {
8+
vi.clearAllMocks();
9+
});
10+
11+
it('fetchByLanguage should fetch stop words by language', async () => {
12+
const mockResponse = [{ id: 1, lang: 'en', stopword: 'example' }];
13+
(fetch as vi.Mock).mockResolvedValue({
14+
ok: true,
15+
json: async () => mockResponse,
16+
});
17+
18+
const result = await fetchByLanguage('en');
19+
expect(fetch).toHaveBeenCalledWith('./api/stopwords?language=en', {
20+
method: 'GET',
21+
headers: {
22+
Accept: 'application/json, text/plain, */*',
23+
'Content-Type': 'application/json',
24+
},
25+
});
26+
expect(result).toEqual(mockResponse);
27+
});
28+
29+
it('postStopWord should post a new stop word', async () => {
30+
const mockResponse = { success: true };
31+
(fetch as vi.Mock).mockResolvedValue({
32+
ok: true,
33+
json: async () => mockResponse,
34+
});
35+
36+
const result = await postStopWord('csrfToken', 'example', 1, 'en');
37+
expect(fetch).toHaveBeenCalledWith('./api/stopword/save', {
38+
method: 'POST',
39+
headers: {
40+
Accept: 'application/json, text/plain, */*',
41+
'Content-Type': 'application/json',
42+
},
43+
body: JSON.stringify({
44+
csrf: 'csrfToken',
45+
stopWord: 'example',
46+
stopWordId: 1,
47+
stopWordsLang: 'en',
48+
}),
49+
});
50+
expect(result).toEqual(mockResponse);
51+
});
52+
53+
it('removeStopWord should delete a stop word', async () => {
54+
const mockResponse = { success: true };
55+
(fetch as vi.Mock).mockResolvedValue({
56+
ok: true,
57+
json: async () => mockResponse,
58+
});
59+
60+
const result = await removeStopWord('csrfToken', 1, 'en');
61+
expect(fetch).toHaveBeenCalledWith('./api/stopword/delete', {
62+
method: 'POST',
63+
headers: {
64+
Accept: 'application/json, text/plain, */*',
65+
'Content-Type': 'application/json',
66+
},
67+
body: JSON.stringify({
68+
csrf: 'csrfToken',
69+
stopWordId: 1,
70+
stopWordsLang: 'en',
71+
}),
72+
});
73+
expect(result).toEqual(mockResponse);
74+
});
75+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Fetch data for stop words
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public License,
5+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
6+
* obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* @package phpMyFAQ
9+
* @author Thorsten Rinne <[email protected]>
10+
* @copyright 2025 phpMyFAQ Team
11+
* @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
12+
* @link https://www.phpmyfaq.de
13+
* @since 2025-02-08
14+
*/
15+
16+
export const fetchByLanguage = async (language: string): Promise<void> => {
17+
try {
18+
const response = await fetch(`./api/stopwords?language=${language}`, {
19+
method: 'GET',
20+
headers: {
21+
Accept: 'application/json, text/plain, */*',
22+
'Content-Type': 'application/json',
23+
},
24+
});
25+
26+
return await response.json();
27+
} catch (error) {
28+
throw error;
29+
}
30+
};
31+
32+
export const postStopWord = async (
33+
csrf: string,
34+
stopWord: string,
35+
stopWordId: number,
36+
stopWordLanguage: string
37+
): Promise<void> => {
38+
try {
39+
const response = await fetch('./api/stopword/save', {
40+
method: 'POST',
41+
headers: {
42+
Accept: 'application/json, text/plain, */*',
43+
'Content-Type': 'application/json',
44+
},
45+
body: JSON.stringify({
46+
csrf: csrf,
47+
stopWord: stopWord,
48+
stopWordId: stopWordId,
49+
stopWordsLang: stopWordLanguage,
50+
}),
51+
});
52+
53+
return await response.json();
54+
} catch (error) {
55+
throw error;
56+
}
57+
};
58+
59+
export const removeStopWord = async (csrf: string, stopWordId: number, stopWordLanguage: string): Promise<void> => {
60+
try {
61+
const response = await fetch('./api/stopword/delete', {
62+
method: 'POST',
63+
headers: {
64+
Accept: 'application/json, text/plain, */*',
65+
'Content-Type': 'application/json',
66+
},
67+
body: JSON.stringify({
68+
csrf: csrf,
69+
stopWordId: stopWordId,
70+
stopWordsLang: stopWordLanguage,
71+
}),
72+
});
73+
74+
return await response.json();
75+
} catch (error) {
76+
throw error;
77+
}
78+
};

phpmyfaq/admin/assets/src/api/upgrade.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface ResponseData {
2222
message?: string;
2323
}
2424

25-
export const fetchHealthCheck = async (): Promise<ResponseData | undefined> => {
25+
export const fetchHealthCheck = async (): Promise<ResponseData> => {
2626
try {
2727
const response = await fetch(`./api/health-check`, {
2828
method: 'GET',

phpmyfaq/admin/assets/src/configuration/configuration.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ export const handleConfiguration = async (): Promise<void> => {
3434
const configTabList: HTMLElement[] = [].slice.call(document.querySelectorAll('#configuration-list a'));
3535
const result = document.getElementById('pmf-configuration-result') as HTMLElement;
3636
if (configTabList.length) {
37-
let tabLoaded = false;
37+
let tabLoaded: boolean = false;
3838
configTabList.forEach((element: HTMLElement): void => {
3939
const configTabTrigger = new Tab(element);
40-
element.addEventListener('shown.bs.tab', async (event) => {
40+
element.addEventListener('shown.bs.tab', async (event: Event): Promise<void> => {
4141
event.preventDefault();
4242
const target = (event.target as HTMLAnchorElement).getAttribute('href') as string;
4343
await handleConfigurationTab(target);
@@ -85,10 +85,10 @@ export const handleConfiguration = async (): Promise<void> => {
8585
};
8686

8787
export const handleSaveConfiguration = async (): Promise<void> => {
88-
const saveConfigurationButton = document.getElementById('save-configuration');
88+
const saveConfigurationButton = document.getElementById('save-configuration') as HTMLButtonElement;
8989

9090
if (saveConfigurationButton) {
91-
saveConfigurationButton.addEventListener('click', async (event) => {
91+
saveConfigurationButton.addEventListener('click', async (event: Event): Promise<void> => {
9292
event.preventDefault();
9393
const form = document.getElementById('configuration-list') as HTMLFormElement;
9494
const formData = new FormData(form);

phpmyfaq/admin/assets/src/configuration/elasticsearch.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const handleElasticsearch = async (): Promise<void> => {
2222
const buttons: NodeListOf<HTMLButtonElement> = document.querySelectorAll('button.pmf-elasticsearch');
2323

2424
if (buttons) {
25-
buttons.forEach((element: HTMLButtonElement) => {
25+
buttons.forEach((element: HTMLButtonElement): void => {
2626
element.addEventListener('click', async (event: Event): Promise<void> => {
2727
event.preventDefault();
2828

@@ -51,11 +51,11 @@ export const handleElasticsearch = async (): Promise<void> => {
5151
const response = (await fetchElasticsearchStatistics()) as unknown as ElasticsearchResponse;
5252

5353
if (response.index) {
54-
const indexName = response.index;
54+
const indexName = response.index as string;
5555
const stats = response.stats;
56-
const count = stats.indices[indexName].total.docs.count;
57-
const sizeInBytes = stats.indices[indexName].total.store.size_in_bytes;
58-
let html = '<dl class="row">';
56+
const count: number = stats.indices[indexName].total.docs.count ?? 0;
57+
const sizeInBytes: number = stats.indices[indexName].total.store.size_in_bytes ?? 0;
58+
let html: string = '<dl class="row">';
5959
html += `<dt class="col-sm-3">Documents</dt><dd class="col-sm-9">${count ?? 0}</dd>`;
6060
html += `<dt class="col-sm-3">Storage size</dt><dd class="col-sm-9">${formatBytes(sizeInBytes ?? 0)}</dd>`;
6161
html += '</dl>';

0 commit comments

Comments
 (0)