gristlabs_grist-core/test/nbrowser/NumericEditor.ts
Dmitry S 93a2d26182 (core) Fix a bug with editing numbers in some locales.
Summary:
Adds a new test for formatting and fix several related bugs it uncovered:
1. When editing a number with "," decimal separator, ensure it opens in
   the editor with "," (rather than ".", the original bug motivating this).
2. When guessing number format, set maxDecimals when it's needed
   (otherwise, e.g. "$1.234", or "4.5%" weren't guessed as numeric)
3. When guessing number format, ignore whitespace when deciding if
   guessed format is correct (otherwise percents can't be guessed in
   locales which add "%" with a non-breaking space before it).

Test Plan: Added a test case that exercises all fixed behaviors.

Reviewers: paulfitz

Reviewed By: paulfitz

Subscribers: paulfitz

Differential Revision: https://phab.getgrist.com/D4177
2024-02-02 22:48:05 -05:00

150 lines
6.2 KiB
TypeScript

import type {UserAPI} from 'app/common/UserAPI';
import * as gu from 'test/nbrowser/gristUtils';
import {setupTestSuite} from 'test/nbrowser/testUtils';
import {assert, driver, Key} from 'mocha-webdriver';
describe('NumericEditor', function() {
this.timeout(20000);
const cleanup = setupTestSuite();
afterEach(() => gu.checkForErrors());
interface Entry {
initial: string;
workaround?: () => Promise<void>;
expect: string;
exp0: string;
expPlain: string;
expFmt: string;
}
interface TestOpts {
localeCode: string;
locale: string;
entriesByColumn: Entry[];
valPlain: string;
valFmt: string;
}
describe('locale en-US', testSuite({
localeCode: 'en-US',
locale: 'United States (English)',
entriesByColumn: [
{initial: '17', expect: '17', exp0: '0', expPlain: '-4.4', expFmt: '-1234.56' },
{initial: '17.500', expect: '17.500', exp0: '0.000', expPlain: '-4.400', expFmt: '-1234.560' },
{initial: '(1.2)', expect: '(1.2)', exp0: ' 0 ', expPlain: '(4.4)', expFmt: '(1234.56)' },
{initial: '1000.456', expect: '1000.456', exp0: '0', expPlain: '-4.4', expFmt: '-1234.56' },
{initial: '1,000.0', expect: '1,000.0', exp0: '0.0', expPlain: '-4.4', expFmt: '-1,234.56' },
{initial: '$5.00', expect: '$5.00', exp0: '$0.00', expPlain: '-$4.40', expFmt: '-$1,234.56'},
{initial: '4.5%', expect: '4.5%', exp0: '0%', expPlain: '-440%', expFmt: '-123,456%' },
],
valPlain: '-4.4',
valFmt: '(1,234.56)',
}));
describe('locale de-DE', testSuite({
localeCode: 'de-DE',
locale: 'Germany (German)',
entriesByColumn: [
{initial: '17', expect: '17', exp0: '0', expPlain: '-4,4', expFmt: '-1234,56' },
{initial: '17,500', expect: '17,500', exp0: '0,000', expPlain: '-4,400', expFmt: '-1234,560' },
{initial: '(1,2)', expect: '(1,2)', exp0: ' 0 ', expPlain: '(4,4)', expFmt: '(1234,56)' },
{initial: '1000,456', expect: '1000,456', exp0: '0', expPlain: '-4,4', expFmt: '-1234,56' },
{initial: '1.000,0', expect: '1.000,0', exp0: '0,0', expPlain: '-4,4', expFmt: '-1.234,56', },
{initial: '5,00€', expect: '5,00 €', exp0: '0,00 €', expPlain: '-4,40 €', expFmt: '-1.234,56 €' },
{initial: '4,5%', expect: '4,5 %', exp0: '0 %', expPlain: '-440 %', expFmt: '-123.456 %' },
],
valPlain: '-4,4',
valFmt: '(1.234,56)',
}));
function testSuite(options: TestOpts) {
const {entriesByColumn} = options;
return function() {
let docId: string;
let api: UserAPI;
let MODKEY: string;
before(async function() {
MODKEY = await gu.modKey();
const session = await gu.session().login();
docId = await session.tempNewDoc(cleanup, `NumericEditor-${options.localeCode}`);
api = session.createHomeApi();
await api.applyUserActions(docId, [
// Make sure there are as many columns as entriesByColumn.
...entriesByColumn.slice(3).map(() => ['AddVisibleColumn', 'Table1', '', {}]),
]);
// Set locale for the document.
await gu.openDocumentSettings();
await driver.findWait('.test-locale-autocomplete', 500).click();
await driver.sendKeys(options.locale, Key.ENTER);
await gu.waitForServer();
assert.equal(await driver.find('.test-locale-autocomplete input').value(), options.locale);
await gu.openPage('Table1');
});
beforeEach(async function() {
// Scroll grid to the left before each test case.
await gu.sendKeys(Key.HOME);
});
it('should create Numeric columns with suitable format', async function() {
// Entering a value into an empty column should switch it to a suitably formatted Numeric.
for (const [i, entry] of Object.entries(entriesByColumn)) {
await gu.getCell({rowNum: 1, col: Number(i)}).click();
await entry.workaround?.();
await gu.enterCell(entry.initial);
assert.equal(await gu.getCell({rowNum: 1, col: Number(i)}).getText(), entry.expect);
}
// Add a new row, which should be filled with 0's. Check what formatted 0's look like.
await gu.sendKeys(Key.chord(MODKEY, Key.ENTER));
await gu.waitForServer();
// Check what 0's look like.
for (const [i, entry] of Object.entries(entriesByColumn)) {
assert.equal(await gu.getCell({rowNum: 2, col: Number(i)}).getText(), entry.exp0);
}
});
it('should support entering plain numbers into a formatted column', async function() {
const rowNum = 3;
const cols = entriesByColumn.map((_, i) => i);
await gu.enterGridRows({rowNum, col: 0},
[ entriesByColumn.map(() => options.valPlain) ]);
assert.deepEqual(await gu.getVisibleGridCells({rowNums: [rowNum], cols}),
entriesByColumn.map((entry) => entry.expPlain));
});
it('should support entering formatted numbers into a formatted column', async function() {
const rowNum = 4;
const cols = entriesByColumn.map((_, i) => i);
await gu.enterGridRows({rowNum, col: 0},
[ entriesByColumn.map(() => options.valFmt) ]);
assert.deepEqual(await gu.getVisibleGridCells({rowNums: [rowNum], cols}),
entriesByColumn.map((entry) => entry.expFmt));
});
it('should allow editing and saving a formatted value', async function() {
for (const [i, entry] of Object.entries(entriesByColumn)) {
await gu.getCell({rowNum: 1, col: Number(i)}).click();
await gu.sendKeys(Key.ENTER);
await gu.waitAppFocus(false);
// Save the value the way it's opened in the editor. It's important that it doesn't
// change interpretation (there was a bug related to this, when a value with "," decimal
// separator would open with a "." decimal separator, and get saved back incorrectly).
await gu.sendKeys(Key.ENTER);
await gu.waitForServer();
await gu.waitAppFocus(true);
assert.equal(await gu.getCell({rowNum: 1, col: Number(i)}).getText(), entry.expect);
}
});
};
}
});