diff --git a/test/cases/i18n/util.mjs b/test/cases/i18n/util.mjs new file mode 100644 index 0000000000000000000000000000000000000000..2aa0cc40750759fd03d5c77e826977d2ebf54476 --- /dev/null +++ b/test/cases/i18n/util.mjs @@ -0,0 +1,295 @@ +import {getGlobal} from "../../../source/types/global.mjs"; + +import * as chai from 'chai'; +import {chaiDom} from "../../util/chai-dom.mjs"; +import {initJSDOM} from "../../util/jsdom.mjs"; + +let expect = chai.expect; +chai.use(chaiDom); + +describe('LocalPicker', function () { + + let LocalPicker, documentLanguage, linkTags, originalLanguages, getPreferredLanguage; + + before(function (done) { + initJSDOM().then(() => { + + documentLanguage = document.documentElement.lang; + linkTags = Array.from(document.querySelectorAll('link[rel="alternate"]')); + linkTags.forEach(item => item.remove()); + + import("element-internals-polyfill").catch(e => done(e)); + + import("../../../source/i18n/util.mjs").then((m) => { + + LocalPicker = m['LocalPicker']; + getPreferredLanguage = m['detectUserLanguagePreference']; + + originalLanguages = navigator.languages; + + done() + }).catch(e => done(e)) + + + }); + }) + + after(function () { + document.documentElement.lang = documentLanguage; + + linkTags.forEach(item => { + const link = document.createElement('link'); + link.setAttribute('rel', 'alternate'); + link.setAttribute('hreflang', item.hreflang); + link.setAttribute('href', item.href); + + // check if already exits + if (!document.querySelector(`link[hreflang="${item.hreflang}"]`)) { + document.querySelector('head').appendChild(link); + } + }); + + + Object.defineProperty(navigator, 'languages', { + value: originalLanguages, + writable: true + }); + + + }) + + // Hilfsfunktion zum Setup eines JSDOM-Dokuments mit <html> und ggf. link-Tags + function setupDOM({htmlLang = '', linkHreflangs = [], navLang = ""}) { + + linkTags = Array.from(document.querySelectorAll('link[rel="alternate"]')); + linkTags.forEach(item => item.remove()); + + // Links hinzufügen + const head = window.document.querySelector('head'); + linkHreflangs.forEach(item => { + const link = window.document.createElement('link'); + link.setAttribute('rel', 'alternate'); + link.setAttribute('hreflang', item.hreflang); + link.setAttribute('href', item.href); + head.appendChild(link); + }); + + window.document.documentElement.lang = htmlLang; + + + Object.defineProperty(navigator, 'language', { + value: navLang[0] || "", + writable: true + }); + + Object.defineProperty(navigator, 'languages', { + value: navLang, + writable: true + }); + } + + describe('getPreferredLanguage()', () => { + + // 1) No document language, no navigator.languages => "No language information available." + it('should return "No language information available." when no current and no user preferences', () => { + setupDOM({htmlLang: '', linkHreflangs: [], navLang: []}); + + const result = getPreferredLanguage(); + expect(result).to.have.property('message').that.not.empty; + }); + + // 2) Document lang is set, but no <link> tags => "No <link> tags with hreflang available." + it('should return "No <link> tags with hreflang available." when there are no link tags', () => { + setupDOM({htmlLang: 'en', linkHreflangs: []}); + window.navigator.languages = []; + + const result = getPreferredLanguage(); + expect(result).to.have.property('current', 'en'); + expect(result).to.have.property('message').that.equals('No <link> tags with hreflang available.'); + }); + + // 3) Document lang 'en', no user preferences, but link tags exist => "None of the preferred languages are available." + it('should return "None of the preferred languages are available." when there are link tags but no matching user preferences', () => { + setupDOM({ + htmlLang: 'en', + linkHreflangs: [ + {hreflang: 'de', href: 'http://example.com/de'}, + {hreflang: 'fr', href: 'http://example.com/fr'} + ], + navLang: [] + }); + + const result = getPreferredLanguage(); + expect(result).to.have.property('current', 'en'); + expect(result).to.have.property('message').that.equals('No available languages match the user\'s preferences.'); + expect(result.available).to.be.an('array').that.has.lengthOf(2); + }); + + // 4) Document lang 'en', user prefers ['en'], link tags with 'en' and 'de' => best match is 'en' + it('should return best match = "en" when user prefers en', () => { + setupDOM({ + htmlLang: 'en', + linkHreflangs: [ + {hreflang: 'en', href: 'http://example.com/en'}, + {hreflang: 'de', href: 'http://example.com/de'} + ] + }); + window.navigator.languages = ['en']; + + const result = getPreferredLanguage(); + expect(result).to.have.property('current', 'en'); + console.log(JSON.stringify(result.preferred)); + expect(result.preferred).to.have.property('full', "en"); + expect(result.preferred).to.have.property('base', "en"); + expect(result.preferred).to.have.property('label', "English"); + expect(result.preferred).to.have.property('href', "http://example.com/en"); + }); + + // 5) Document lang 'de-DE', user prefers ['de-DE'], link tags with 'de-DE' & 'en-US' => best match is 'de-DE' + it('should return best match = "de-DE" when user prefers de-DE', () => { + setupDOM({ + htmlLang: 'de-DE', + linkHreflangs: [ + {hreflang: 'de-DE', href: 'http://example.com/de-DE'}, + {hreflang: 'en-US', href: 'http://example.com/en-US'} + ] + }); + window.navigator.languages = ['de-DE']; + + const result = getPreferredLanguage(); + expect(result).to.have.property('current', 'de-DE'); + + expect(result.preferred).to.have.property('full', "de-DE"); + expect(result.preferred).to.have.property('base', "de"); + expect(result.preferred).to.have.property('label', "Deutsch (Deutschland)"); + expect(result.preferred).to.have.property('href', "http://example.com/de-DE"); + + }); + + + // 5x) Document lang 'de-DE', user prefers ['de-DE'], link tags with 'de-DE' & 'en-US' => best match is 'de-DE' + it('should return best match = "de-DE" when user prefers de-DE', () => { + setupDOM({ + htmlLang: 'en', + linkHreflangs: [ + {hreflang: 'de', href: 'http://example.com/de-DE'}, + {hreflang: 'en', href: 'http://example.com/en-US'} + ] + }); + window.navigator.languages = ['de-DE']; + + const result = getPreferredLanguage(); + expect(result).to.have.property('current', 'en'); + + expect(result.preferred).to.have.property('full', "de"); + expect(result.preferred).to.have.property('base', "de"); + expect(result.preferred).to.have.property('label', "Deutsch"); + expect(result.preferred).to.have.property('href', "http://example.com/de-DE"); + + }); + + // 6) Document lang 'de-DE', user prefers ['en-US', 'en'], link tags with 'en-US' & 'en-GB' => best match = 'en-US' + it('should return best match = "en-US" for user preferences [en-US, en]', () => { + setupDOM({ + htmlLang: 'de-DE', + linkHreflangs: [ + {hreflang: 'en-US', href: 'http://example.com/en-US'}, + {hreflang: 'en-GB', href: 'http://example.com/en-GB'} + ] + }); + window.navigator.languages = ['en-US', 'en']; + + const result = getPreferredLanguage(); + expect(result).to.have.property('current', 'de-DE'); + expect(result.preferred).to.have.property('full', "en-US"); + expect(result.preferred).to.have.property('base', "en"); + expect(result.preferred).to.have.property('label', "English (United States)"); + expect(result.preferred).to.have.property('href', "http://example.com/en-US"); + + }); + + // 7) Document lang 'de-DE', user prefers ['de','en'], link tags with 'de-DE' & 'en-US' => best match = 'de-DE' (baseLang = 'de') + it('should return best match = "de-DE" when user preferences include its base language "de"', () => { + setupDOM({ + htmlLang: 'de-DE', + linkHreflangs: [ + {hreflang: 'de-DE', href: 'http://example.com/de-DE'}, + {hreflang: 'en-US', href: 'http://example.com/en-US'} + ] + }); + window.navigator.languages = ['de', 'en']; + + const result = getPreferredLanguage(); + expect(result).to.have.property('current', 'de-DE'); + + expect(result.preferred).to.have.property('full', "de-DE"); + expect(result.preferred).to.have.property('base', "de"); + expect(result.preferred).to.have.property('label', "Deutsch (Deutschland)"); + expect(result.preferred).to.have.property('href', "http://example.com/de-DE"); + + }); + + // 8) Multiple link tags share the same weight => the first in the sorted array should be returned + it('should return the first item when multiple link tags have the same weight', () => { + setupDOM({ + htmlLang: 'en', + linkHreflangs: [ + {hreflang: 'fr', href: 'http://example.com/fr'}, + {hreflang: 'es', href: 'http://example.com/es'} + ] + }); + // Neither "fr" nor "es" is in the user preferences => both have weight = 1 + window.navigator.languages = ['de']; + + const result = getPreferredLanguage(); + // Both 'fr' and 'es' have weight 1. After sorting, 'fr' appears first (as it was added first). + expect(result.available[0].fullLang).to.equal('fr'); + expect(result.available[1].fullLang).to.equal('es'); + expect(result.offerable).to.be.undefined; + expect(result).to.have.property('message', 'No available languages match the user\'s preferences.'); + }); + + // 9) Check that bestURL is returned if a best match is found + it('should include bestURL when a best match is found', () => { + setupDOM({ + htmlLang: 'fr', + linkHreflangs: [ + {hreflang: 'fr-FR', href: 'http://example.com/fr-FR'}, + {hreflang: 'de-DE', href: 'http://example.com/de-DE'} + ] + }); + window.navigator.languages = ['fr-FR', 'de-DE']; + + const result = getPreferredLanguage(); + + expect(result.preferred).to.have.property('full', "fr-FR"); + expect(result.preferred).to.have.property('base', "fr"); + expect(result.preferred).to.have.property('label', "Français (France)"); + expect(result.preferred).to.have.property('href', "http://example.com/fr-FR"); + + }); + + // 10) Check presence of availableLanguages array, ensuring it has weight info + it('should return availableLanguages with weight for each link', () => { + setupDOM({ + htmlLang: 'en-GB', + linkHreflangs: [ + {hreflang: 'en-GB', href: 'http://example.com/en-GB'}, + {hreflang: 'en-US', href: 'http://example.com/en-US'} + ] + }); + window.navigator.languages = ['en-GB', 'en', 'en-US']; + + const result = getPreferredLanguage(); + expect(result.available).to.be.an('array').with.lengthOf(2); + result.available.forEach(item => { + expect(item).to.have.property('weight'); + expect(item).to.have.property('fullLang'); + expect(item).to.have.property('baseLang'); + expect(item).to.have.property('href'); + }); + }); + + }); + +}) \ No newline at end of file