2021-05-03 23:24:40 +00:00
|
|
|
// Copyright 2021 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import { assert } from 'chai';
|
|
|
|
import * as moment from 'moment';
|
2021-09-18 00:30:08 +00:00
|
|
|
import { setupI18n } from '../../util/setupI18n';
|
2021-05-03 23:24:40 +00:00
|
|
|
import enMessages from '../../../_locales/en/messages.json';
|
|
|
|
import esMessages from '../../../_locales/es/messages.json';
|
|
|
|
import nbMessages from '../../../_locales/nb/messages.json';
|
|
|
|
import nnMessages from '../../../_locales/nn/messages.json';
|
|
|
|
import ptBrMessages from '../../../_locales/pt_BR/messages.json';
|
|
|
|
import zhCnMessages from '../../../_locales/zh_CN/messages.json';
|
|
|
|
|
|
|
|
import * as expirationTimer from '../../util/expirationTimer';
|
|
|
|
|
|
|
|
describe('expiration timer utilities', () => {
|
|
|
|
const i18n = setupI18n('en', enMessages);
|
|
|
|
|
|
|
|
describe('DEFAULT_DURATIONS_IN_SECONDS', () => {
|
|
|
|
const { DEFAULT_DURATIONS_IN_SECONDS } = expirationTimer;
|
|
|
|
|
|
|
|
it('includes at least 3 durations', () => {
|
|
|
|
assert.isAtLeast(DEFAULT_DURATIONS_IN_SECONDS.length, 3);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('includes 1 hour as seconds', () => {
|
|
|
|
const oneHour = moment.duration(1, 'hour').asSeconds();
|
|
|
|
assert.include(DEFAULT_DURATIONS_IN_SECONDS, oneHour);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('format', () => {
|
|
|
|
const { format } = expirationTimer;
|
|
|
|
|
|
|
|
it('handles an undefined duration', () => {
|
|
|
|
assert.strictEqual(format(i18n, undefined), 'off');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles no duration', () => {
|
|
|
|
assert.strictEqual(format(i18n, 0), 'off');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('formats durations', () => {
|
|
|
|
new Map<number, string>([
|
|
|
|
[1, '1 second'],
|
|
|
|
[2, '2 seconds'],
|
|
|
|
[30, '30 seconds'],
|
|
|
|
[59, '59 seconds'],
|
|
|
|
[moment.duration(1, 'm').asSeconds(), '1 minute'],
|
|
|
|
[moment.duration(5, 'm').asSeconds(), '5 minutes'],
|
|
|
|
[moment.duration(1, 'h').asSeconds(), '1 hour'],
|
|
|
|
[moment.duration(8, 'h').asSeconds(), '8 hours'],
|
|
|
|
[moment.duration(1, 'd').asSeconds(), '1 day'],
|
|
|
|
[moment.duration(6, 'd').asSeconds(), '6 days'],
|
|
|
|
[moment.duration(8, 'd').asSeconds(), '8 days'],
|
|
|
|
[moment.duration(30, 'd').asSeconds(), '30 days'],
|
|
|
|
[moment.duration(365, 'd').asSeconds(), '365 days'],
|
|
|
|
[moment.duration(1, 'w').asSeconds(), '1 week'],
|
|
|
|
[moment.duration(3, 'w').asSeconds(), '3 weeks'],
|
|
|
|
[moment.duration(52, 'w').asSeconds(), '52 weeks'],
|
|
|
|
]).forEach((expected, input) => {
|
|
|
|
assert.strictEqual(format(i18n, input), expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('formats other languages successfully', () => {
|
|
|
|
const esI18n = setupI18n('es', esMessages);
|
|
|
|
assert.strictEqual(format(esI18n, 120), '2 minutos');
|
|
|
|
|
|
|
|
const zhCnI18n = setupI18n('zh_CN', zhCnMessages);
|
|
|
|
assert.strictEqual(format(zhCnI18n, 60), '1 分钟');
|
|
|
|
|
|
|
|
// The underlying library supports the "pt" locale, not the "pt_BR" locale. That's
|
|
|
|
// what we're testing here.
|
|
|
|
const ptBrI18n = setupI18n('pt_BR', ptBrMessages);
|
|
|
|
assert.strictEqual(
|
|
|
|
format(ptBrI18n, moment.duration(5, 'days').asSeconds()),
|
|
|
|
'5 dias'
|
|
|
|
);
|
|
|
|
|
|
|
|
// The underlying library supports the Norwegian language, which is a macrolanguage
|
|
|
|
// for Bokmål and Nynorsk.
|
|
|
|
[setupI18n('nb', nbMessages), setupI18n('nn', nnMessages)].forEach(
|
|
|
|
norwegianI18n => {
|
|
|
|
assert.strictEqual(
|
|
|
|
format(norwegianI18n, moment.duration(6, 'hours').asSeconds()),
|
|
|
|
'6 timer'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('falls back to English if the locale is not supported', () => {
|
|
|
|
const badI18n = setupI18n('bogus', {});
|
|
|
|
assert.strictEqual(format(badI18n, 120), '2 minutes');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles a "mix" of units gracefully', () => {
|
|
|
|
// We don't expect there to be a "mix" of units, but we shouldn't choke if a bad
|
|
|
|
// client gives us an unexpected timestamp.
|
|
|
|
const mix = moment
|
|
|
|
.duration(6, 'days')
|
|
|
|
.add(moment.duration(2, 'hours'))
|
|
|
|
.asSeconds();
|
|
|
|
assert.strictEqual(format(i18n, mix), '6 days, 2 hours');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles negative numbers gracefully', () => {
|
|
|
|
// The proto helps enforce non-negative numbers by specifying a u32, but because
|
|
|
|
// JavaScript lacks such a type, we test it here.
|
|
|
|
assert.strictEqual(format(i18n, -1), '1 second');
|
|
|
|
assert.strictEqual(format(i18n, -120), '2 minutes');
|
|
|
|
assert.strictEqual(format(i18n, -0), 'off');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles fractional seconds gracefully', () => {
|
|
|
|
// The proto helps enforce integer numbers by specifying a u32, but this function
|
|
|
|
// shouldn't choke if bad data is passed somehow.
|
|
|
|
assert.strictEqual(format(i18n, 4.2), '4 seconds');
|
|
|
|
assert.strictEqual(format(i18n, 4.8), '4 seconds');
|
|
|
|
assert.strictEqual(format(i18n, 0.2), '1 second');
|
|
|
|
assert.strictEqual(format(i18n, 0.8), '1 second');
|
|
|
|
|
|
|
|
// If multiple things go wrong and we pass a fractional negative number, we still
|
|
|
|
// shouldn't explode.
|
|
|
|
assert.strictEqual(format(i18n, -4.2), '4 seconds');
|
|
|
|
assert.strictEqual(format(i18n, -4.8), '4 seconds');
|
|
|
|
assert.strictEqual(format(i18n, -0.2), '1 second');
|
|
|
|
assert.strictEqual(format(i18n, -0.8), '1 second');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|