Skip to content

Commit 321ca86

Browse files
fix: Tests coverage reporting issue (#4)
* fix: tests coverage reporting
1 parent 1f3681d commit 321ca86

File tree

2 files changed

+113
-70
lines changed

2 files changed

+113
-70
lines changed

src/js/translations.js

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
let translations = {};
22

3-
async function loadTranslations(lang) {
3+
const loadTranslations = async (lang) => {
44
try {
55
const response = await fetch(`./locales/${lang}.json`);
66
if (!response.ok) throw new Error("Translation file not found");
@@ -13,65 +13,92 @@ async function loadTranslations(lang) {
1313
}
1414
return {};
1515
}
16-
}
16+
};
1717

18-
function getNestedProperty(obj, path) {
18+
const getNestedProperty = (obj, path) => {
1919
return path.split(".").reduce((current, key) => current?.[key], obj);
20-
}
20+
};
2121

22-
function getBrowserLanguage() {
22+
const getBrowserLanguage = () => {
2323
const browserLang = navigator.language.split("-")[0];
2424
const supportedLangs = ["en", "es", "fr", "pt"];
2525
return supportedLangs.includes(browserLang) ? browserLang : "en";
26-
}
26+
};
2727

28-
function getCurrentLanguage() {
28+
const getCurrentLanguage = () => {
2929
return localStorage.getItem("language") || getBrowserLanguage();
30-
}
31-
32-
async function setLanguage(lang) {
33-
translations = await loadTranslations(lang);
34-
35-
localStorage.setItem("language", lang);
30+
};
3631

37-
document.title = translations.title;
32+
const updateDocumentMeta = (lang, translationsData) => {
33+
document.title = translationsData.title;
3834

3935
document
4036
.querySelector('meta[name="description"]')
41-
.setAttribute("content", translations.metaDescription);
37+
.setAttribute("content", translationsData.metaDescription);
4238
document
4339
.querySelector('meta[property="og:description"]')
44-
.setAttribute("content", translations.metaDescription);
45-
document.querySelector('meta[property="og:title"]').setAttribute("content", translations.title);
40+
.setAttribute("content", translationsData.metaDescription);
41+
document
42+
.querySelector('meta[property="og:title"]')
43+
.setAttribute("content", translationsData.title);
4644

45+
document.documentElement.setAttribute("lang", lang);
46+
};
47+
48+
const updateTranslations = (translationsData) => {
4749
document.querySelectorAll("[data-translate]").forEach((element) => {
4850
const key = element.getAttribute("data-translate");
49-
const value = getNestedProperty(translations, key);
51+
const value = getNestedProperty(translationsData, key);
5052
if (value) {
5153
element.textContent = value;
5254
}
5355
});
5456

5557
document.querySelectorAll("[data-translate-alt]").forEach((element) => {
5658
const key = element.getAttribute("data-translate-alt");
57-
const value = getNestedProperty(translations, key);
59+
const value = getNestedProperty(translationsData, key);
5860
if (value) {
5961
element.setAttribute("alt", value);
6062
}
6163
});
6264

6365
document.querySelectorAll("[data-translate-html]").forEach((element) => {
6466
const key = element.getAttribute("data-translate-html");
65-
const value = getNestedProperty(translations, key);
67+
const value = getNestedProperty(translationsData, key);
6668
if (value) {
6769
element.innerHTML = value;
6870
}
6971
});
72+
};
7073

71-
document.documentElement.setAttribute("lang", lang);
72-
}
74+
const setLanguage = async (lang) => {
75+
translations = await loadTranslations(lang);
76+
localStorage.setItem("language", lang);
77+
updateDocumentMeta(lang, translations);
78+
updateTranslations(translations);
79+
};
7380

7481
document.addEventListener("DOMContentLoaded", async () => {
7582
const currentLang = getCurrentLanguage();
7683
await setLanguage(currentLang);
7784
});
85+
86+
// Export for Node.js/Jest (not executed in browser)
87+
/* eslint-disable no-undef */
88+
if (typeof module !== "undefined" && module.exports) {
89+
module.exports = {
90+
loadTranslations,
91+
getNestedProperty,
92+
getBrowserLanguage,
93+
getCurrentLanguage,
94+
updateDocumentMeta,
95+
updateTranslations,
96+
setLanguage
97+
};
98+
/* eslint-enable no-undef */
99+
}
100+
101+
if (typeof window !== "undefined") {
102+
window.setLanguage = setLanguage;
103+
window.getCurrentLanguage = getCurrentLanguage;
104+
}

src/js/translations.test.js

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,36 @@
22
* @jest-environment jsdom
33
*/
44

5+
const {
6+
loadTranslations,
7+
getNestedProperty,
8+
getBrowserLanguage,
9+
getCurrentLanguage,
10+
updateDocumentMeta,
11+
updateTranslations,
12+
setLanguage
13+
} = require("./translations");
14+
515
global.fetch = jest.fn();
616
console.error = jest.fn();
717

818
describe("Translations Module", () => {
919
beforeEach(() => {
1020
jest.clearAllMocks();
1121
localStorage.clear();
22+
document.body.innerHTML = "";
23+
document.head.innerHTML =
24+
'<title>Test</title><meta name="description" content="Test"><meta property="og:title" content="Test"><meta property="og:description" content="Test">';
1225
});
1326

1427
describe("Core Helper Functions", () => {
1528
test("getNestedProperty - should handle nested translation keys", () => {
16-
const getNestedProperty = (obj, path) =>
17-
path.split(".").reduce((current, key) => current?.[key], obj);
18-
1929
const translations = { user: { profile: { name: "Jane" } } };
20-
2130
expect(getNestedProperty(translations, "user.profile.name")).toBe("Jane");
2231
expect(getNestedProperty(translations, "missing.key")).toBeUndefined();
2332
});
2433

2534
test("getBrowserLanguage - should return supported language or default to English", () => {
26-
const getBrowserLanguage = () => {
27-
const browserLang = navigator.language.split("-")[0];
28-
const supportedLangs = ["en", "es", "fr", "pt"];
29-
return supportedLangs.includes(browserLang) ? browserLang : "en";
30-
};
31-
3235
Object.defineProperty(navigator, "language", { value: "es-ES", configurable: true });
3336
expect(getBrowserLanguage()).toBe("es");
3437

@@ -40,73 +43,86 @@ describe("Translations Module", () => {
4043
describe("Translation Loading", () => {
4144
test("should successfully fetch translations", async () => {
4245
const mockData = { title: "Test Title", role: "Developer" };
43-
global.fetch.mockResolvedValueOnce({
44-
ok: true,
45-
json: async () => mockData
46-
});
46+
global.fetch.mockResolvedValueOnce({ ok: true, json: async () => mockData });
4747

48-
const response = await fetch("./locales/en.json");
49-
const data = await response.json();
48+
const data = await loadTranslations("en");
5049

50+
expect(fetch).toHaveBeenCalledWith("./locales/en.json");
5151
expect(data).toEqual(mockData);
5252
});
5353

5454
test("should handle fetch errors gracefully", async () => {
5555
global.fetch.mockRejectedValueOnce(new Error("Network error"));
56+
global.fetch.mockResolvedValueOnce({
57+
ok: true,
58+
json: async () => ({ title: "English" })
59+
});
60+
61+
const data = await loadTranslations("fr");
5662

57-
try {
58-
await fetch("./locales/es.json");
59-
} catch (error) {
60-
expect(error.message).toBe("Network error");
61-
}
63+
expect(console.error).toHaveBeenCalled();
64+
expect(data).toEqual({ title: "English" });
6265
});
6366
});
6467

6568
describe("localStorage Integration", () => {
6669
test("should store and retrieve selected language", () => {
6770
localStorage.setItem("language", "es");
68-
expect(localStorage.getItem("language")).toBe("es");
71+
expect(getCurrentLanguage()).toBe("es");
72+
});
6973

70-
localStorage.setItem("language", "fr");
71-
expect(localStorage.getItem("language")).toBe("fr");
74+
test("should fall back to browser language if no stored language", () => {
75+
Object.defineProperty(navigator, "language", { value: "fr-FR", configurable: true });
76+
expect(getCurrentLanguage()).toBe("fr");
7277
});
7378
});
7479

7580
describe("DOM Updates", () => {
76-
beforeEach(() => {
77-
document.documentElement.innerHTML = `
78-
<head>
79-
<title>Original Title</title>
80-
<meta name="description" content="Original Description">
81-
</head>
82-
<body>
83-
<h1 data-translate="role">Original Role</h1>
84-
<img data-translate-alt="imageAlt" alt="Original Alt">
85-
</body>
86-
`;
87-
});
88-
8981
test("should update document title and meta tags", () => {
90-
document.title = "New Title";
91-
const metaDesc = document.querySelector('meta[name="description"]');
92-
metaDesc.setAttribute("content", "New Description");
82+
const translationsData = { title: "New Title", metaDescription: "New Description" };
83+
updateDocumentMeta("es", translationsData);
9384

9485
expect(document.title).toBe("New Title");
95-
expect(metaDesc.getAttribute("content")).toBe("New Description");
86+
expect(document.querySelector('meta[name="description"]').getAttribute("content")).toBe(
87+
"New Description"
88+
);
89+
expect(document.documentElement.getAttribute("lang")).toBe("es");
9690
});
9791

9892
test("should update elements with data-translate attribute", () => {
99-
const element = document.querySelector('[data-translate="role"]');
100-
element.textContent = "Software Developer";
101-
102-
expect(element.textContent).toBe("Software Developer");
93+
document.body.innerHTML = '<p data-translate="role">Default</p>';
94+
updateTranslations({ role: "Software Developer" });
95+
expect(document.querySelector('[data-translate="role"]').textContent).toBe(
96+
"Software Developer"
97+
);
10398
});
10499

105100
test("should update alt attributes with data-translate-alt", () => {
106-
const img = document.querySelector('[data-translate-alt="imageAlt"]');
107-
img.setAttribute("alt", "New Alt Text");
101+
document.body.innerHTML = '<img data-translate-alt="imageAlt" alt="Default">';
102+
updateTranslations({ imageAlt: "Profile picture" });
103+
expect(
104+
document.querySelector('[data-translate-alt="imageAlt"]').getAttribute("alt")
105+
).toBe("Profile picture");
106+
});
107+
});
108+
109+
describe("setLanguage Integration", () => {
110+
test("should load translations and update DOM", async () => {
111+
const mockTranslations = {
112+
title: "Título",
113+
metaDescription: "Descripción",
114+
role: "Desarrollador"
115+
};
116+
global.fetch.mockResolvedValueOnce({ ok: true, json: async () => mockTranslations });
117+
document.body.innerHTML = '<p data-translate="role">Default</p>';
108118

109-
expect(img.getAttribute("alt")).toBe("New Alt Text");
119+
await setLanguage("es");
120+
121+
expect(localStorage.getItem("language")).toBe("es");
122+
expect(document.title).toBe("Título");
123+
expect(document.querySelector('[data-translate="role"]').textContent).toBe(
124+
"Desarrollador"
125+
);
110126
});
111127
});
112128
});

0 commit comments

Comments
 (0)