From 93c19395c10ab5fe8dfbf6aa50553f8f5d63b966 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 16 Jun 2020 10:09:52 +1000 Subject: [PATCH] Change back to old window syntax and allow stubbing of it --- ts/global.d.ts | 71 ------------ ts/session/crypto/MessageEncrypter.ts | 13 +-- ts/session/protocols/SessionProtocol.ts | 15 +-- ts/session/sending/MessageSender.ts | 7 +- ts/session/utils/Groups.ts | 5 +- ts/session/utils/SyncMessageUtils.ts | 5 +- ts/test/test-utils/testUtils.ts | 30 ++++- ts/window.d.ts | 76 +++++++++++++ ts/window/index.ts | 142 ------------------------ 9 files changed, 118 insertions(+), 246 deletions(-) create mode 100644 ts/window.d.ts delete mode 100644 ts/window/index.ts diff --git a/ts/global.d.ts b/ts/global.d.ts index 500047422..f8321457f 100644 --- a/ts/global.d.ts +++ b/ts/global.d.ts @@ -1,74 +1,3 @@ -// TODO: Delete this and depend on window.ts instead -interface Window { - CONSTANTS: any; - versionInfo: any; - - Events: any; - Lodash: any; - clearLocalData: any; - getAccountManager: any; - getConversations: any; - getFriendsFromContacts: any; - mnemonic: any; - clipboard: any; - attemptConnection: any; - - passwordUtil: any; - userConfig: any; - shortenPubkey: any; - - dcodeIO: any; - libsignal: any; - libloki: any; - displayNameRegex: any; - - Signal: any; - Whisper: any; - ConversationController: any; - MessageController: any; - - StringView: any; - - // Following function needs to be written in background.js - // getMemberList: any; - - onLogin: any; - setPassword: any; - textsecure: any; - Session: any; - log: any; - i18n: any; - friends: any; - generateID: any; - storage: any; - pushToast: any; - - confirmationDialog: any; - showQRDialog: any; - showSeedDialog: any; - showPasswordDialog: any; - showEditProfileDialog: any; - - deleteAccount: any; - - toggleTheme: any; - toggleMenuBar: any; - toggleSpellCheck: any; - toggleLinkPreview: any; - toggleMediaPermissions: any; - - getSettingValue: any; - setSettingValue: any; - lokiFeatureFlags: any; - - resetDatabase: any; - restart: () => void; - - lokiFileServerAPI: any; - WebAPI: any; - SenderKeyAPI: any; -} - interface Promise { ignore(): void; } diff --git a/ts/session/crypto/MessageEncrypter.ts b/ts/session/crypto/MessageEncrypter.ts index 833d8c59b..9a9471e1a 100644 --- a/ts/session/crypto/MessageEncrypter.ts +++ b/ts/session/crypto/MessageEncrypter.ts @@ -1,6 +1,5 @@ import { EncryptionType } from '../types/EncryptionType'; import { SignalService } from '../../protobuf'; -import { libloki, libsignal, Signal, textsecure } from '../../window'; import { UserUtil } from '../../util'; import { CipherTextObject } from '../../../libtextsecure/libsignal-protocol'; @@ -46,7 +45,7 @@ export async function encrypt( cipherText: Uint8Array; }> { const plainText = padPlainTextBuffer(plainTextBuffer); - const address = new libsignal.SignalProtocolAddress(device, 1); + const address = new window.libsignal.SignalProtocolAddress(device, 1); if (encryptionType === EncryptionType.MediumGroup) { // TODO: Do medium group stuff here @@ -55,11 +54,11 @@ export async function encrypt( let innerCipherText: CipherTextObject; if (encryptionType === EncryptionType.SessionRequest) { - const cipher = new libloki.crypto.FallBackSessionCipher(address); + const cipher = new window.libloki.crypto.FallBackSessionCipher(address); innerCipherText = await cipher.encrypt(plainText.buffer); } else { - const cipher = new libsignal.SessionCipher( - textsecure.storage.protocol, + const cipher = new window.libsignal.SessionCipher( + window.textsecure.storage.protocol, address ); innerCipherText = await cipher.encrypt(plainText.buffer); @@ -85,8 +84,8 @@ async function encryptUsingSealedSender( senderDevice: 1, }); - const cipher = new Signal.Metadata.SecretSessionCipher( - textsecure.storage.protocol + const cipher = new window.Signal.Metadata.SecretSessionCipher( + window.textsecure.storage.protocol ); const cipherTextBuffer = await cipher.encrypt( device, diff --git a/ts/session/protocols/SessionProtocol.ts b/ts/session/protocols/SessionProtocol.ts index da0f95129..8be5b7462 100644 --- a/ts/session/protocols/SessionProtocol.ts +++ b/ts/session/protocols/SessionProtocol.ts @@ -1,7 +1,6 @@ import { SessionRequestMessage } from '../messages/outgoing'; // import { MessageSender } from '../sending'; import { createOrUpdateItem, getItemById } from '../../../js/modules/data'; -import { libloki, libsignal, textsecure } from '../../window'; import { MessageSender } from '../sending'; import { MessageUtils } from '../utils'; import { PubKey } from '../types'; @@ -50,9 +49,9 @@ export class SessionProtocol { /** Returns true if we already have a session with that device */ public static async hasSession(pubkey: PubKey): Promise { // Session does not use the concept of a deviceId, thus it's always 1 - const address = new libsignal.SignalProtocolAddress(pubkey.key, 1); - const sessionCipher = new libsignal.SessionCipher( - textsecure.storage.protocol, + const address = new window.libsignal.SignalProtocolAddress(pubkey.key, 1); + const sessionCipher = new window.libsignal.SessionCipher( + window.textsecure.storage.protocol, address ); @@ -90,7 +89,7 @@ export class SessionProtocol { return; } - const preKeyBundle = await libloki.storage.getPreKeyBundleForContact( + const preKeyBundle = await window.libloki.storage.getPreKeyBundleForContact( pubkey.key ); @@ -102,11 +101,7 @@ export class SessionProtocol { try { await SessionProtocol.sendSessionRequest(sessionReset, pubkey); } catch (error) { - window.console.warn( - 'Failed to send session request to:', - pubkey.key, - error - ); + console.warn('Failed to send session request to:', pubkey.key, error); } } diff --git a/ts/session/sending/MessageSender.ts b/ts/session/sending/MessageSender.ts index e0439a85c..0cc3d6cae 100644 --- a/ts/session/sending/MessageSender.ts +++ b/ts/session/sending/MessageSender.ts @@ -5,7 +5,6 @@ import { OpenGroupMessage } from '../messages/outgoing'; import { SignalService } from '../../protobuf'; import { UserUtil } from '../../util'; import { MessageEncrypter } from '../crypto'; -import { lokiMessageAPI, lokiPublicChatAPI } from '../../window'; import pRetry from 'p-retry'; // ================ Regular ================ @@ -15,7 +14,7 @@ import pRetry from 'p-retry'; */ export function canSendToSnode(): boolean { // Seems like lokiMessageAPI is not always guaranteed to be initialized - return Boolean(lokiMessageAPI); + return Boolean(window.lokiMessageAPI); } /** @@ -42,7 +41,7 @@ export async function send( const data = wrapEnvelope(envelope); return pRetry( - async () => lokiMessageAPI.sendMessage(device, data, timestamp, ttl), + async () => window.lokiMessageAPI.sendMessage(device, data, timestamp, ttl), { retries: Math.max(attempts - 1, 0), factor: 1, @@ -102,7 +101,7 @@ export async function sendToOpenGroup( This should be fixed and we shouldn't rely on returning true/false, rather return nothing (success) or throw an error (failure) */ const { group, quote, attachments, preview, body } = message; - const channelAPI = await lokiPublicChatAPI.findOrCreateChannel( + const channelAPI = await window.lokiPublicChatAPI.findOrCreateChannel( group.server, group.channel, group.conversationId diff --git a/ts/session/utils/Groups.ts b/ts/session/utils/Groups.ts index 2a0d130ba..d73104b63 100644 --- a/ts/session/utils/Groups.ts +++ b/ts/session/utils/Groups.ts @@ -1,8 +1,7 @@ -import { ConversationController } from '../../window'; import { PubKey } from '../types'; export async function getGroupMembers(groupId: PubKey): Promise> { - const groupConversation = ConversationController.get(groupId.key); + const groupConversation = window.ConversationController.get(groupId.key); const groupMembers = groupConversation ? groupConversation.attributes.members : undefined; @@ -15,7 +14,7 @@ export async function getGroupMembers(groupId: PubKey): Promise> { } export function isMediumGroup(groupId: PubKey): boolean { - const conversation = ConversationController.get(groupId.key); + const conversation = window.ConversationController.get(groupId.key); if (!conversation) { return false; diff --git a/ts/session/utils/SyncMessageUtils.ts b/ts/session/utils/SyncMessageUtils.ts index 92c1116c0..596df601c 100644 --- a/ts/session/utils/SyncMessageUtils.ts +++ b/ts/session/utils/SyncMessageUtils.ts @@ -4,7 +4,6 @@ import { getAllConversations, getPrimaryDeviceFor, } from '../../../js/modules/data'; -import { ConversationController, Whisper } from '../../window'; import { ContentMessage, SyncMessage } from '../messages/outgoing'; @@ -32,7 +31,7 @@ export async function getSyncContacts(): Promise | undefined> { const primaryDevice = await getPrimaryDeviceFor(thisDevice); const conversations = await getAllConversations({ - ConversationCollection: Whisper.ConversationCollection, + ConversationCollection: window.Whisper.ConversationCollection, }); // We are building a set of all contacts @@ -54,7 +53,7 @@ export async function getSyncContacts(): Promise | undefined> { ); const seondaryContactsPromise = secondaryContactsPartial.map(async c => - ConversationController.getOrCreateAndWait( + window.ConversationController.getOrCreateAndWait( c.getPrimaryDevicePubKey(), 'private' ) diff --git a/ts/test/test-utils/testUtils.ts b/ts/test/test-utils/testUtils.ts index 4cfbc50ac..be8f3113d 100644 --- a/ts/test/test-utils/testUtils.ts +++ b/ts/test/test-utils/testUtils.ts @@ -4,10 +4,10 @@ import * as window from '../../window'; import * as DataShape from '../../../js/modules/data'; import { v4 as uuid } from 'uuid'; -import { ImportMock } from 'ts-mock-imports'; import { PubKey } from '../../../ts/session/types'; import { ChatMessage } from '../../session/messages/outgoing'; +const globalAny: any = global; const sandbox = sinon.createSandbox(); // We have to do this in a weird way because Data uses module.exports @@ -26,7 +26,7 @@ export function stubData(fn: keyof DataFunction): sinon.SinonStub { return sandbox.stub(Data, fn); } -type WindowFunction = typeof window; +type WindowValue = Partial | undefined; /** * Stub a window object. @@ -34,15 +34,33 @@ type WindowFunction = typeof window; * Note: This uses a custom sandbox. * Please call `restoreStubs()` or `stub.restore()` to restore original functionality. */ -export function stubWindow( +export function stubWindow( fn: K, - replaceWith?: Partial + value: WindowValue ) { - return ImportMock.mockOther(window, fn, replaceWith); + // tslint:disable-next-line: no-typeof-undefined + if (typeof globalAny.window === 'undefined') { + globalAny.window = {}; + } + + const set = (newValue: WindowValue) => { + globalAny.window[fn] = newValue; + }; + + const get = () => { + return globalAny.window[fn] as WindowValue; + }; + + globalAny.window[fn] = value; + + return { + get, + set, + }; } export function restoreStubs() { - ImportMock.restore(); + globalAny.window = undefined; sandbox.restore(); } diff --git a/ts/window.d.ts b/ts/window.d.ts new file mode 100644 index 000000000..4e85bfc75 --- /dev/null +++ b/ts/window.d.ts @@ -0,0 +1,76 @@ +import { LocalizerType } from '../types/Util'; +import LokiMessageAPI from '../../js/modules/loki_message_api'; +import LokiPublicChatFactoryAPI from '../../js/modules/loki_public_chat_api'; +import { LibsignalProtocol } from '../../libtextsecure/libsignal-protocol'; +import { SignalInterface } from '../../js/modules/signal'; + +/* +We declare window stuff here instead of global.d.ts because we are importing other declarations. +If you import anything in global.d.ts, the type system won't work correctly. +*/ +declare global { + interface Window { + CONSTANTS: any; + ConversationController: any; + Events: any; + Lodash: any; + LokiAppDotNetServerAPI: any; + LokiFileServerAPI: any; + LokiPublicChatAPI: any; + LokiRssAPI: any; + LokiSnodeAPI: any; + MessageController: any; + SenderKeyAPI: any; + Session: any; + Signal: SignalInterface; + StringView: any; + StubAppDotNetApi: any; + StubMessageAPI: any; + WebAPI: any; + Whisper: any; + attemptConnection: any; + clearLocalData: any; + clipboard: any; + confirmationDialog: any; + dcodeIO: any; + deleteAccount: any; + displayNameRegex: any; + friends: any; + generateID: any; + getAccountManager: any; + getConversations: any; + getFriendsFromContacts: any; + getSettingValue: any; + i18n: LocalizerType; + libloki: any; + libsignal: LibsignalProtocol; + log: any; + lokiFeatureFlags: any; + lokiFileServerAPI: any; + lokiMessageAPI: LokiMessageAPI; + lokiPublicChatAPI: LokiPublicChatFactoryAPI; + mnemonic: any; + onLogin: any; + passwordUtil: any; + pushToast: any; + resetDatabase: any; + restart: any; + seedNodeList: any; + setPassword: any; + setSettingValue: any; + shortenPubkey: any; + showEditProfileDialog: any; + showPasswordDialog: any; + showQRDialog: any; + showSeedDialog: any; + storage: any; + textsecure: any; + toggleLinkPreview: any; + toggleMediaPermissions: any; + toggleMenuBar: any; + toggleSpellCheck: any; + toggleTheme: any; + userConfig: any; + versionInfo: any; + } +} diff --git a/ts/window/index.ts b/ts/window/index.ts deleted file mode 100644 index 4688cde7b..000000000 --- a/ts/window/index.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { LocalizerType } from '../types/Util'; -import LokiMessageAPI from '../../js/modules/loki_message_api'; -import LokiPublicChatFactoryAPI from '../../js/modules/loki_public_chat_api'; -import { LibsignalProtocol } from '../../libtextsecure/libsignal-protocol'; -import { SignalInterface } from '../../js/modules/signal'; - -interface WindowInterface extends Window { - seedNodeList: any; - - WebAPI: any; - LokiSnodeAPI: any; - SenderKeyAPI: any; - StubMessageAPI: any; - StubAppDotNetApi: any; - LokiPublicChatAPI: any; - LokiAppDotNetServerAPI: any; - LokiFileServerAPI: any; - LokiRssAPI: any; - - CONSTANTS: any; - versionInfo: any; - - Events: any; - Lodash: any; - clearLocalData: any; - getAccountManager: any; - getConversations: any; - getFriendsFromContacts: any; - mnemonic: any; - clipboard: any; - attemptConnection: any; - - passwordUtil: any; - userConfig: any; - shortenPubkey: any; - - dcodeIO: any; - libsignal: LibsignalProtocol; - libloki: any; - displayNameRegex: any; - - Signal: SignalInterface; - Whisper: any; - ConversationController: any; - - onLogin: any; - setPassword: any; - textsecure: any; - Session: any; - log: any; - i18n: LocalizerType; - friends: any; - generateID: any; - storage: any; - pushToast: any; - - confirmationDialog: any; - showQRDialog: any; - showSeedDialog: any; - showPasswordDialog: any; - showEditProfileDialog: any; - - deleteAccount: any; - - toggleTheme: any; - toggleMenuBar: any; - toggleSpellCheck: any; - toggleLinkPreview: any; - toggleMediaPermissions: any; - - getSettingValue: any; - setSettingValue: any; - lokiFeatureFlags: any; - - resetDatabase: any; - - lokiMessageAPI: LokiMessageAPI; - lokiPublicChatAPI: LokiPublicChatFactoryAPI; -} - -// In the case for tests -// tslint:disable-next-line: no-typeof-undefined -if (typeof window === 'undefined') { - const globalAny: any = global; - globalAny.window = {}; -} - -declare const window: WindowInterface; - -// TODO: Is there an easier way to dynamically export these? - -// Utilities -export const WebAPI = window.WebAPI; -export const Events = window.Events; -export const Signal = window.Signal; -export const Whisper = window.Whisper; -export const ConversationController = window.ConversationController; -export const passwordUtil = window.passwordUtil; - -// Values -export const CONSTANTS = window.CONSTANTS; -export const versionInfo = window.versionInfo; -export const mnemonic = window.mnemonic; -export const lokiFeatureFlags = window.lokiFeatureFlags; - -// Getters -export const getAccountManager = window.getAccountManager; -export const getConversations = window.getConversations; -export const getFriendsFromContacts = window.getFriendsFromContacts; -export const getSettingValue = window.getSettingValue; - -// Setters -export const setPassword = window.setPassword; -export const setSettingValue = window.setSettingValue; - -// UI Events -export const pushToast = window.pushToast; -export const confirmationDialog = window.confirmationDialog; - -export const showQRDialog = window.showQRDialog; -export const showSeedDialog = window.showSeedDialog; -export const showPasswordDialog = window.showPasswordDialog; -export const showEditProfileDialog = window.showEditProfileDialog; - -export const toggleTheme = window.toggleTheme; -export const toggleMenuBar = window.toggleMenuBar; -export const toggleSpellCheck = window.toggleSpellCheck; -export const toggleLinkPreview = window.toggleLinkPreview; -export const toggleMediaPermissions = window.toggleMediaPermissions; - -// Actions -export const clearLocalData = window.clearLocalData; -export const deleteAccount = window.deleteAccount; -export const resetDatabase = window.resetDatabase; -export const attemptConnection = window.attemptConnection; - -export const libloki = window.libloki; -export const libsignal = window.libsignal; -export const textsecure = window.textsecure; - -export const lokiMessageAPI = window.lokiMessageAPI; -export const lokiPublicChatAPI = window.lokiPublicChatAPI;