Read and write preferred reactions to storage
This commit is contained in:
parent
4e3b64ef64
commit
20be8a11fe
9 changed files with 208 additions and 71 deletions
|
@ -1,43 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { DEFAULT_PREFERRED_REACTION_EMOJI_SHORT_NAMES } from '../../reactions/constants';
|
||||
|
||||
import { getPreferredReactionEmoji } from '../../reactions/getPreferredReactionEmoji';
|
||||
|
||||
describe('getPreferredReactionEmoji', () => {
|
||||
it('returns the default set if passed anything invalid', () => {
|
||||
[
|
||||
// Invalid types
|
||||
undefined,
|
||||
null,
|
||||
DEFAULT_PREFERRED_REACTION_EMOJI_SHORT_NAMES.join(','),
|
||||
// Invalid lengths
|
||||
[],
|
||||
DEFAULT_PREFERRED_REACTION_EMOJI_SHORT_NAMES.slice(0, 3),
|
||||
[...DEFAULT_PREFERRED_REACTION_EMOJI_SHORT_NAMES, '✨'],
|
||||
// Non-strings in the array
|
||||
['❤️', '👍', undefined, '😂', '😮', '😢'],
|
||||
['❤️', '👍', 99, '😂', '😮', '😢'],
|
||||
// Invalid emoji
|
||||
['❤️', '👍', 'x', '😂', '😮', '😢'],
|
||||
['❤️', '👍', 'garbage!!', '😂', '😮', '😢'],
|
||||
['❤️', '👍', '✨✨', '😂', '😮', '😢'],
|
||||
].forEach(input => {
|
||||
assert.deepStrictEqual(getPreferredReactionEmoji(input, 2), [
|
||||
'❤️',
|
||||
'👍🏼',
|
||||
'👎🏼',
|
||||
'😂',
|
||||
'😮',
|
||||
'😢',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a custom set if passed a valid value', () => {
|
||||
const input = ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'];
|
||||
assert.deepStrictEqual(getPreferredReactionEmoji(input, 3), input);
|
||||
});
|
||||
});
|
82
ts/test-both/reactions/preferredReactionEmoji_test.ts
Normal file
82
ts/test-both/reactions/preferredReactionEmoji_test.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
||||
import {
|
||||
canBeSynced,
|
||||
getPreferredReactionEmoji,
|
||||
} from '../../reactions/preferredReactionEmoji';
|
||||
|
||||
describe('preferred reaction emoji utilities', () => {
|
||||
describe('getPreferredReactionEmoji', () => {
|
||||
const defaultsForSkinTone2 = ['❤️', '👍🏼', '👎🏼', '😂', '😮', '😢'];
|
||||
|
||||
it('returns the default set if passed a non-array', () => {
|
||||
[undefined, null, '❤️👍🏼👎🏼😂😮😢'].forEach(input => {
|
||||
assert.deepStrictEqual(
|
||||
getPreferredReactionEmoji(input, 2),
|
||||
defaultsForSkinTone2
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the default set if passed an empty array', () => {
|
||||
assert.deepStrictEqual(
|
||||
getPreferredReactionEmoji([], 2),
|
||||
defaultsForSkinTone2
|
||||
);
|
||||
});
|
||||
|
||||
it('falls back to defaults if passed an array that is too short', () => {
|
||||
const input = ['✨', '❇️'];
|
||||
const expected = ['✨', '❇️', '👎🏽', '😂', '😮', '😢'];
|
||||
assert.deepStrictEqual(getPreferredReactionEmoji(input, 3), expected);
|
||||
});
|
||||
|
||||
it('falls back to defaults when passed an array with some invalid values', () => {
|
||||
const input = ['✨', 'invalid', '🎇', '🦈', undefined, ''];
|
||||
const expected = ['✨', '👍🏼', '🎇', '🦈', '😮', '😢'];
|
||||
assert.deepStrictEqual(getPreferredReactionEmoji(input, 2), expected);
|
||||
});
|
||||
|
||||
it('returns a custom set if passed a valid value', () => {
|
||||
const input = ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'];
|
||||
assert.deepStrictEqual(getPreferredReactionEmoji(input, 3), input);
|
||||
});
|
||||
|
||||
it('only returns the first few emoji if passed a value that is too long', () => {
|
||||
const expected = ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'];
|
||||
const input = [...expected, '💅', '💅', '💅', '💅'];
|
||||
assert.deepStrictEqual(getPreferredReactionEmoji(input, 3), expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('canBeSynced', () => {
|
||||
it('returns false for non-arrays', () => {
|
||||
assert.isFalse(canBeSynced(undefined));
|
||||
assert.isFalse(canBeSynced(null));
|
||||
assert.isFalse(canBeSynced('❤️👍🏼👎🏼😂😮😢'));
|
||||
});
|
||||
|
||||
it('returns false for arrays that are too long', () => {
|
||||
assert.isFalse(canBeSynced(Array(21).fill('🦊')));
|
||||
});
|
||||
|
||||
it('returns false for arrays that have items that are too long', () => {
|
||||
const input = ['✨', '❇️', 'x'.repeat(21), '🦈', '💖', '🅿️'];
|
||||
assert.isFalse(canBeSynced(input));
|
||||
});
|
||||
|
||||
it('returns true for valid values', () => {
|
||||
[
|
||||
[],
|
||||
['💅'],
|
||||
['✨', '❇️', '🎇', '🦈', '💖', '🅿️'],
|
||||
['this', 'array', 'has', 'no', 'emoji', 'but', "that's", 'okay'],
|
||||
].forEach(input => {
|
||||
assert.isTrue(canBeSynced(input));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -243,13 +243,33 @@ describe('preferred reactions duck', () => {
|
|||
describe('savePreferredReactions', () => {
|
||||
const { savePreferredReactions } = actions;
|
||||
|
||||
// We want to create a fake ConversationController for testing purposes, and we need
|
||||
// to sidestep typechecking to do that.
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
let storagePutStub: sinon.SinonStub;
|
||||
let captureChangeStub: sinon.SinonStub;
|
||||
let oldConversationController: any;
|
||||
|
||||
beforeEach(() => {
|
||||
storagePutStub = sinonSandbox.stub(window.storage, 'put').resolves();
|
||||
|
||||
oldConversationController = window.ConversationController;
|
||||
|
||||
captureChangeStub = sinonSandbox.stub();
|
||||
window.ConversationController = {
|
||||
getOurConversationOrThrow: (): any => ({
|
||||
captureChange: captureChangeStub,
|
||||
}),
|
||||
} as any;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.ConversationController = oldConversationController;
|
||||
});
|
||||
|
||||
describe('thunk', () => {
|
||||
it('saves the preferred reaction emoji to storage', async () => {
|
||||
it('saves the preferred reaction emoji to local storage', async () => {
|
||||
await savePreferredReactions()(
|
||||
sinon.spy(),
|
||||
() => getRootState(stateWithOpenCustomizationModal),
|
||||
|
@ -264,6 +284,16 @@ describe('preferred reactions duck', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('on success, enqueues a storage service upload', async () => {
|
||||
await savePreferredReactions()(
|
||||
sinon.spy(),
|
||||
() => getRootState(stateWithOpenCustomizationModal),
|
||||
null
|
||||
);
|
||||
|
||||
sinon.assert.calledOnce(captureChangeStub);
|
||||
});
|
||||
|
||||
it('on success, dispatches a pending action followed by a fulfilled action', async () => {
|
||||
const dispatch = sinon.spy();
|
||||
await savePreferredReactions()(
|
||||
|
@ -299,6 +329,18 @@ describe('preferred reactions duck', () => {
|
|||
type: 'preferredReactions/SAVE_PREFERRED_REACTIONS_REJECTED',
|
||||
});
|
||||
});
|
||||
|
||||
it('on failure, does not enqueue a storage service upload', async () => {
|
||||
storagePutStub.rejects(new Error('something went wrong'));
|
||||
|
||||
await savePreferredReactions()(
|
||||
sinon.spy(),
|
||||
() => getRootState(stateWithOpenCustomizationModal),
|
||||
null
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(captureChangeStub);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SAVE_PREFERRED_REACTIONS_FULFILLED', () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue