gristlabs_grist-core/test/nbrowser/LanguageSettings.ts
Jarosław Sadziński 90d3ee037a (core) User language switcher
Summary:
New language selector on the Account page for logged-in users.
New icon for switching language for an anonymous user.

For anonymous users, language is stored in a cookie grist_user_locale.
Language is stored in user settings for authenticated users and takes
precedence over what is stored in the cookie.

Test Plan: New tests

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D3766
2023-01-26 09:47:14 +01:00

257 lines
10 KiB
TypeScript

import {assert, createDriver, driver, WebDriver} from 'mocha-webdriver';
import * as gu from 'test/nbrowser/gristUtils';
import {server, setupTestSuite} from 'test/nbrowser/testUtils';
describe("LanguageSettings", function() {
this.timeout('50s');
const cleanup = setupTestSuite();
before(async function() {
if (server.isExternalServer()) {
this.skip();
}
});
// List of languages that chrome supports https://developer.chrome.com/docs/webstore/i18n/#localeTable
const locales = [ // [language to set in the browser, country code detected, language name detected]
['fr', 'FR', 'Français'],
['te', 'US', 'English'], // Telugu is not supported yet, so Grist should fallback to English (US).
['en', 'US', 'English'], // This is a default language for Grist.
['pt-BR', 'BR', 'Português (Brasil)']
];
for (const [locale, countryCode, language] of locales) {
describe(`correctly detects browser language ${locale}`, () => {
// Change the language to the one we want to test.
withLang(locale);
before(async function() {
const session = await gu.session().personalSite.anon.login();
await session.loadRelPath("/");
await gu.waitForDocMenuToLoad();
});
it("shows correct language from browser settings", async () => {
// Find the button to switch the language.
const button = await langButton();
assert.isTrue(await button.isDisplayed());
// Make sure correct flag is shown.
const flag = await button.find("div").getCssValue("background-image");
assert.isTrue(flag.endsWith(countryCode + '.svg")'), `Flag is ${flag} search for ${countryCode}`);
// Make sure we see the all languages in the menu.
await button.click();
const menu = await gu.currentDriver().findWait(".grist-floating-menu", 100);
const allLangues = (await menu.findAll("li", e => e.getText())).map(l => l.toLowerCase());
for (const [, , language] of locales) {
assert.include(allLangues, language.toLowerCase());
}
// Make sure that this language is selected.
assert.equal(await selectedLang(), language.toLowerCase());
// Smoke test that we see the correct language.
const welcomeText = await gu.currentDriver().find(".test-welcome-title").getText();
if (locale === 'en') {
assert.equal(welcomeText, "Welcome to Grist!");
} else if (locale === 'fr') {
assert.equal(welcomeText, "Bienvenue sur Grist !");
}
});
});
}
describe("for Anonymous", function() {
before(async function() {
const session = await gu.session().personalSite.anon.login();
await session.loadRelPath("/");
await gu.waitForDocMenuToLoad();
});
it("allows anonymous user to switch a language", async () => {
await langButton().click();
// By default we have English (US) selected.
assert.equal(await selectedLang(), "english");
// Change to French.
await gu.currentDriver().find(".test-language-lang-fr").click();
// We will be reloaded, so wait until we see the new language.
await waitForLangButton("fr");
// Now we have a cookie with the language selected, so reloading the page should keep it.
await gu.currentDriver().navigate().refresh();
await gu.waitForDocMenuToLoad();
await waitForLangButton("fr");
assert.equal(await languageInCookie(), "fr");
// Switch to German.
await langButton().click();
await gu.currentDriver().find(".test-language-lang-de").click();
await waitForLangButton("de");
// Make sure we see new cookie.
assert.equal(await languageInCookie(), "de");
// Remove the cookie and reload.
await clearCookie();
await gu.currentDriver().navigate().refresh();
await gu.waitForDocMenuToLoad();
// Make sure we see the default language.
await waitForLangButton("en");
// Test if changing the cookie is reflected in the UI. This cookie is available for javascript.
await setCookie("fr");
await gu.currentDriver().navigate().refresh();
await gu.waitForDocMenuToLoad();
await waitForLangButton("fr");
assert.equal(await languageInCookie(), "fr");
// Go back to English.
await clearCookie();
await gu.currentDriver().navigate().refresh();
await gu.waitForDocMenuToLoad();
});
it("when user is logged in the language is still taken from the cookie", async () => {
await langButton().click();
// By default we have English (US) selected ()
assert.equal(await selectedLang(), "english");
// Now login to the account.
const user = await gu.session().personalSite.user('user1').login();
await user.loadRelPath("/");
await gu.waitForDocMenuToLoad();
// Language should still be english.
await waitForHiddenButton("en");
// And we should not have a cookie.
assert.isNull(await languageInCookie());
// Go back to anonymous.
const anonym = await gu.session().personalSite.anon.login();
await anonym.loadRelPath("/");
await gu.waitForDocMenuToLoad();
// Change language to french.
await langButton().click();
await driver.find(".test-language-lang-fr").click();
await waitForLangButton("fr");
// Login as user.
await user.login();
await anonym.loadRelPath("/");
await gu.waitForDocMenuToLoad();
// Language should still be french.
await waitForHiddenButton("fr");
// But now we should have a cookie (cookie is reused).
assert.equal(await languageInCookie(), 'fr');
await clearCookie();
});
});
describe("for logged in user with nb-NO", function() {
withLang("de");
let session: gu.Session;
before(async function() {
session = await gu.session().login();
await session.loadRelPath("/");
await gu.waitForDocMenuToLoad();
});
after(async function() {
await clearCookie();
const api = session.createHomeApi();
await api.updateUserLocale(null);
});
it("profile page detects correct language", async () => {
const driver = gu.currentDriver();
// Make sure we don't have a cookie yet.
assert.isNull(await languageInCookie());
// Or a saved setting.
let gristConfig: any = await driver.executeScript("return window.gristConfig");
assert.isNull(gristConfig.userLocale);
await gu.openProfileSettingsPage();
// Make sure we see the correct language.
assert.equal(await languageMenu().getText(), "Deutsch");
// Make sure we see hidden indicator.
await waitForHiddenButton("de");
// Change language to nb-.NO
await languageMenu().click();
await driver.findContentWait('.test-select-menu li', 'Norsk bokmål (Norge)', 100).click();
// This is api call and we will be reloaded, so wait for the hidden indicator.
await waitForHiddenButton("nb-NO");
// Now we should have a cookie.
assert.equal(await languageInCookie(), "nb-NO");
// And the gristConfig should have this language.
gristConfig = await driver.executeScript("return window.gristConfig");
assert.equal(gristConfig.userLocale, "nb-NO");
// If we remove the cookie, we should still use the gristConfig.
await clearCookie();
await driver.navigate().refresh();
await waitForHiddenButton("nb-NO");
// If we set a different cookie, we should still use the saved setting.
await setCookie("de");
await driver.navigate().refresh();
await waitForHiddenButton("nb-NO");
// Make sure this works on the document, by adding a new doc and smoke testing the Add New button.
await session.tempNewDoc(cleanup, "Test");
assert.equal(await driver.findWait(".test-dp-add-new", 3000).getText(), "Legg til ny");
});
});
});
function languageMenu() {
return gu.currentDriver().find('.test-account-page-language .test-select-open');
}
async function clearCookie() {
await gu.currentDriver().executeScript(
"document.cookie = 'grist_user_locale=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';");
}
async function setCookie(locale: string) {
await gu.currentDriver().executeScript(
`document.cookie = 'grist_user_locale=${locale}; expires=Thu, 01 Jan 2970 00:00:00 UTC; path=/;';`);
}
async function waitForLangButton(locale: string) {
await gu.waitToPass(async () =>
assert.isTrue(await gu.currentDriver().findWait(`.test-language-current-${locale}`, 1000).isDisplayed()), 4000);
}
async function waitForHiddenButton(locale: string) {
await gu.waitToPass(async () =>
assert.isTrue(await gu.currentDriver().findWait(`input.test-language-current-${locale}`, 1000).isPresent()), 4000);
}
async function languageInCookie(): Promise<string | null> {
const cookie2: string = await gu.currentDriver().executeScript("return document.cookie");
return cookie2.match(/grist_user_locale=([^;]+)/)?.[1] ?? null;
}
function withLang(locale: string) {
let customDriver: WebDriver;
let oldLanguage: string | undefined;
before(async function() {
// On Mac we can't change the language, so skip the test.
if (await gu.isMac()) { return this.skip(); }
oldLanguage = process.env.LANGUAGE;
// How to run chrome with a different language:
// https://developer.chrome.com/docs/extensions/reference/i18n/#how-to-set-browsers-locale
process.env.LANGUAGE = locale;
customDriver = await createDriver({
extraArgs: [
'lang=' + locale,
...(process.env.MOCHA_WEBDRIVER_HEADLESS ? [`headless=chrome`] : [])
]
});
server.setDriver(customDriver);
gu.setDriver(customDriver);
const session = await gu.session().personalSite.anon.login();
await session.loadRelPath("/");
await gu.waitForDocMenuToLoad();
});
after(async function() {
if (await gu.isMac()) { return this.skip(); }
gu.setDriver();
server.setDriver();
await customDriver.quit();
process.env.LANGUAGE = oldLanguage;
});
}
function langButton() {
return gu.currentDriver().findWait(".test-language-button", 500);
}
async function selectedLang() {
const menu = gu.currentDriver().findWait(".grist-floating-menu", 100);
return (await menu.find(".test-language-selected").findClosest("li").getText()).toLowerCase();
}