From bbc13d058e32ffb681315ee1434fc3d0c0424a93 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Thu, 9 Dec 2021 09:06:04 +0100 Subject: [PATCH] Update electron to 16.0.4 --- .github/workflows/benchmark.yml | 2 +- .github/workflows/ci.yml | 16 +- .gitignore | 2 - .nvmrc | 2 +- CONTRIBUTING.md | 2 +- Gruntfile.js | 283 --- app/main.ts | 32 +- app/user_config.ts | 14 +- libtextsecure/test/.eslintrc.js | 21 - libtextsecure/test/_test.js | 70 - libtextsecure/test/fake_web_api.js | 59 - libtextsecure/test/index.html | 44 - package.json | 16 +- preload.js | 2 +- preload_test.js | 35 +- test/index.html | 68 +- test/{_test.js => test.js} | 42 +- ts/build/test-electron.ts | 54 + ts/build/test-release.ts | 80 + ts/environment.ts | 3 +- ts/logging/main_process_logging.ts | 4 +- ts/services/calling.ts | 10 +- ts/test-both/environment_test.ts | 5 - ts/test-electron/state/ducks/calling_test.ts | 2 +- .../textsecure/SendMessage_test.ts | 46 +- ts/util/lint/exceptions.json | 1569 +---------------- ts/util/lint/linter.ts | 10 +- yarn.lock | 983 ++--------- 28 files changed, 491 insertions(+), 2985 deletions(-) delete mode 100644 libtextsecure/test/.eslintrc.js delete mode 100644 libtextsecure/test/_test.js delete mode 100644 libtextsecure/test/fake_web_api.js delete mode 100644 libtextsecure/test/index.html rename test/{_test.js => test.js} (55%) create mode 100644 ts/build/test-electron.ts create mode 100644 ts/build/test-release.ts rename libtextsecure/test/sendmessage_test.js => ts/test-electron/textsecure/SendMessage_test.ts (76%) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 249de28d6..bef012256 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -31,7 +31,7 @@ jobs: - name: Setup node.js uses: actions/setup-node@v2 with: - node-version: '16.5.0' + node-version: '16.9.1' - name: Install global dependencies run: npm install -g yarn@1.22.10 typescript@4.4.2 ts-node@10.2.1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8bd9af4d..44a1bc05b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '16.5.0' + node-version: '16.9.1' - run: npm install -g yarn@1.22.10 - name: Cache Desktop node_modules @@ -43,7 +43,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '16.5.0' + node-version: '16.9.1' - run: npm install -g yarn@1.22.10 - name: Cache Desktop node_modules @@ -63,7 +63,7 @@ jobs: run: yarn electron:install-app-deps - run: yarn test-node - run: yarn test-electron - - run: yarn grunt test-release:osx + - run: yarn test-release env: NODE_ENV: production @@ -77,7 +77,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '16.5.0' + node-version: '16.9.1' - run: sudo apt-get install xvfb - run: npm install -g yarn@1.22.10 @@ -99,7 +99,7 @@ jobs: env: LANG: en_US LANGUAGE: en_US - - run: xvfb-run --auto-servernum yarn grunt test-release:linux + - run: xvfb-run --auto-servernum yarn test-release env: NODE_ENV: production @@ -114,7 +114,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '16.5.0' + node-version: '16.9.1' - run: npm install -g yarn@1.22.10 - name: Cache Desktop node_modules @@ -135,7 +135,7 @@ jobs: - run: type temp.json | findstr /v certificateSubjectName | findstr /v certificateSha1 > package.json - run: yarn prepare-beta-build - run: yarn build - - run: node build\grunt.js test - - run: node build\grunt.js test-release:win + - run: yarn test-electron + - run: yarn test-release env: SIGNAL_ENV: production diff --git a/.gitignore b/.gitignore index 0b7a35ae9..4388c1a0a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,9 +21,7 @@ tsconfig.tsbuildinfo js/components.js js/util_worker.js libtextsecure/components.js -libtextsecure/test/test.js stylesheets/*.css -test/test.js /storybook-static/ preload.bundle.* ts/sql/mainWorker.bundle.js.LICENSE.txt diff --git a/.nvmrc b/.nvmrc index 28ebe8b4d..06e751596 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.5.0 +16.9.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62352e3cd..0746e38f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -281,7 +281,7 @@ yarn generate yarn build ``` -Then, run the tests using `grunt test-release:osx --dir=release`, replacing `osx` with `linux` or `win` depending on your platform. +Then, run the tests using `yarn test-release`. ## Translations diff --git a/Gruntfile.js b/Gruntfile.js index 95d620457..be50217cc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,36 +1,14 @@ // Copyright 2014-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -const { join } = require('path'); const importOnce = require('node-sass-import-once'); const rimraf = require('rimraf'); const mkdirp = require('mkdirp'); -const spectron = require('spectron'); -const asar = require('asar'); -const fs = require('fs'); -const os = require('os'); -const assert = require('assert'); const sass = require('node-sass'); -const packageJson = require('./package.json'); /* eslint-disable more/no-then, no-console */ module.exports = grunt => { - async function promiseToAsyncGruntTask(promise, gruntDone) { - let succeeded = false; - try { - await promise; - succeeded = true; - } catch (err) { - grunt.log.error(err); - } - if (succeeded) { - gruntDone(); - } else { - gruntDone(false); - } - } - const bower = grunt.file.readJSON('bower.json'); const components = []; // eslint-disable-next-line guard-for-in, no-restricted-syntax @@ -47,23 +25,6 @@ module.exports = grunt => { src: components, dest: 'js/components.js', }, - test: { - src: [ - 'node_modules/mocha/mocha.js', - 'node_modules/chai/chai.js', - 'test/_test.js', - ], - dest: 'test/test.js', - }, - libtextsecuretest: { - src: [ - 'node_modules/jquery/dist/jquery.js', - 'node_modules/mocha/mocha.js', - 'node_modules/chai/chai.js', - 'libtextsecure/test/_test.js', - ], - dest: 'libtextsecure/test/test.js', - }, }, sass: { options: { @@ -116,24 +77,6 @@ module.exports = grunt => { cmd: 'yarn build-protobuf', }, }, - 'test-release': { - osx: { - archive: `mac/${packageJson.productName}.app/Contents/Resources/app.asar`, - exe: `mac/${packageJson.productName}.app/Contents/MacOS/${packageJson.productName}`, - }, - mas: { - archive: 'mas/Signal.app/Contents/Resources/app.asar', - exe: `mas/${packageJson.productName}.app/Contents/MacOS/${packageJson.productName}`, - }, - linux: { - archive: 'linux-unpacked/resources/app.asar', - exe: `linux-unpacked/${packageJson.name}`, - }, - win: { - archive: 'win-unpacked/resources/app.asar', - exe: `win-unpacked/${packageJson.productName}.exe`, - }, - }, gitinfo: {}, // to be populated by grunt gitinfo }); @@ -187,238 +130,12 @@ module.exports = grunt => { mkdirp.sync('release'); }); - async function runTests(environment) { - const { Application } = spectron; - const electronBinary = - process.platform === 'win32' ? 'electron.cmd' : 'electron'; - - const path = join(__dirname, 'node_modules', '.bin', electronBinary); - const args = [join(__dirname, 'app', 'main.js')]; - grunt.log.writeln('Starting path', path, 'with args', args); - const app = new Application({ - path, - args, - env: { - NODE_ENV: environment, - }, - requireName: 'unused', - startTimeout: 30000, - }); - - function getMochaResults() { - // eslint-disable-next-line no-undef - return window.mochaResults; - } - - async function logForFailure() { - const temporaryDirectory = join( - os.tmpdir(), - `Signal-Desktop-tests--${Date.now()}-${Math.random() - .toString() - .slice(2)}` - ); - const renderProcessLogPath = join( - temporaryDirectory, - 'render-process.log' - ); - const mainProcessLogPath = join(temporaryDirectory, 'main-process.log'); - - await fs.promises.mkdir(temporaryDirectory, { recursive: true }); - - await Promise.all([ - (async () => { - const logs = await app.client.getRenderProcessLogs(); - await fs.promises.writeFile( - renderProcessLogPath, - logs.map(log => JSON.stringify(log)).join('\n') - ); - })(), - (async () => { - const logs = await app.client.getMainProcessLogs(); - await fs.promises.writeFile(mainProcessLogPath, logs.join('\n')); - })(), - ]); - - console.error(); - grunt.log.error( - `Renderer process logs written to ${renderProcessLogPath}` - ); - grunt.log.error(`Renderer process logs written to ${mainProcessLogPath}`); - grunt.log.error( - `For easier debugging, try NODE_ENV='${environment}' yarn start` - ); - console.error(); - } - - try { - await app.start(); - - grunt.log.writeln('App started. Now waiting for test results...'); - await app.client.waitUntil( - () => - app.client.execute(getMochaResults).then(data => Boolean(data.value)), - 25000, - 'Expected to find window.mochaResults set!' - ); - - const results = (await app.client.execute(getMochaResults)).value; - if (!results) { - await logForFailure(); - throw new Error("Couldn't extract test results"); - } - - if (results.failures > 0) { - const errorMessage = `Found ${results.failures} failing test${ - results.failures === 1 ? '' : 's' - }.`; - grunt.log.error(errorMessage); - results.reports.forEach(report => { - grunt.log.error(JSON.stringify(report, null, 2)); - }); - await logForFailure(); - throw new Error(errorMessage); - } - - grunt.log.ok(`${results.passes} tests passed.`); - } finally { - if (app.isRunning()) { - await app.stop(); - } - } - } - - grunt.registerTask( - 'unit-tests', - 'Run unit tests w/Electron', - function thisNeeded() { - const environment = grunt.option('env') || 'test'; - promiseToAsyncGruntTask(runTests(environment), this.async()); - } - ); - - grunt.registerTask( - 'lib-unit-tests', - 'Run libtextsecure unit tests w/Electron', - function thisNeeded() { - const environment = grunt.option('env') || 'test-lib'; - promiseToAsyncGruntTask(runTests(environment), this.async()); - } - ); - - grunt.registerMultiTask( - 'test-release', - 'Test packaged releases', - function thisNeeded() { - const dir = grunt.option('dir') || 'release'; - const environment = grunt.option('env') || 'production'; - const config = this.data; - const archive = [dir, config.archive].join('/'); - const files = [ - 'config/default.json', - `config/${environment}.json`, - `config/local-${environment}.json`, - ]; - - console.log(this.target, archive); - const releaseFiles = files.concat(config.files || []); - releaseFiles.forEach(fileName => { - console.log(fileName); - try { - asar.statFile(archive, fileName); - return true; - } catch (e) { - console.log(e); - throw new Error(`Missing file ${fileName}`); - } - }); - - if (config.appUpdateYML) { - const appUpdateYML = [dir, config.appUpdateYML].join('/'); - if (fs.existsSync(appUpdateYML)) { - console.log('auto update ok'); - } else { - throw new Error(`Missing auto update config ${appUpdateYML}`); - } - } - - const done = this.async(); - // A simple test to verify a visible window is opened with a title - const { Application } = spectron; - - const path = [dir, config.exe].join('/'); - console.log('Starting path', path); - const app = new Application({ - path, - }); - - const sleep = millis => - new Promise(resolve => setTimeout(resolve, millis)); - - Promise.race([app.start(), sleep(15000)]) - .then(() => { - if (!app.isRunning()) { - throw new Error('Application failed to start'); - } - - return app.client.getWindowCount(); - }) - .then(count => { - assert.equal(count, 1); - console.log('window opened'); - }) - .then(() => - // Verify the window's title - app.client.waitUntil( - async () => - (await app.client.getTitle()) === packageJson.productName, - { - timeoutMsg: `Expected window title to be ${JSON.stringify( - packageJson.productName - )}`, - } - ) - ) - .then(() => { - console.log('title ok'); - }) - .then(() => { - assert( - app.chromeDriver.logLines.indexOf(`NODE_ENV ${environment}`) > -1 - ); - console.log('environment ok'); - }) - .then( - () => - // Successfully completed test - app.stop(), - error => - // Test failed! - app.stop().then(() => { - grunt.fail.fatal(`Test failed: ${error.message} ${error.stack}`); - }) - ) - .catch(error => { - console.log('Main process logs:'); - app.client.getMainProcessLogs().then(logs => { - logs.forEach(log => { - console.log(log); - }); - - // Test failed! - grunt.fail.fatal(`Failure! ${error.message} ${error.stack}`); - }); - }) - .then(done); - } - ); - grunt.registerTask('tx', [ 'exec:tx-pull-mostly-translated', 'exec:tx-pull-any-existing-translation', 'locale-patch', ]); grunt.registerTask('dev', ['default', 'watch']); - grunt.registerTask('test', ['unit-tests', 'lib-unit-tests']); grunt.registerTask('date', ['gitinfo', 'getExpireTime']); grunt.registerTask('default', [ 'exec:build-protobuf', diff --git a/app/main.ts b/app/main.ts index aa05d7584..d0e415bbd 100644 --- a/app/main.ts +++ b/app/main.ts @@ -23,6 +23,7 @@ import { screen, shell, systemPreferences, + desktopCapturer, } from 'electron'; import { z } from 'zod'; @@ -587,11 +588,10 @@ async function createWindow() { if (getEnvironment() === Environment.Test) { mainWindow.loadURL( - prepareFileUrl([__dirname, '../test/index.html'], moreKeys) - ); - } else if (getEnvironment() === Environment.TestLib) { - mainWindow.loadURL( - prepareFileUrl([__dirname, '../libtextsecure/test/index.html'], moreKeys) + prepareFileUrl([__dirname, '../test/index.html'], { + ...moreKeys, + argv: JSON.stringify(process.argv), + }) ); } else { mainWindow.loadURL( @@ -1424,10 +1424,7 @@ app.on('ready', async () => { addSensitivePath(userDataPath); - if ( - getEnvironment() !== Environment.Test && - getEnvironment() !== Environment.TestLib - ) { + if (getEnvironment() !== Environment.Test) { installFileHandler({ protocol: electronProtocol, userDataPath, @@ -2120,3 +2117,20 @@ ipc.handle('show-save-dialog', async (_event, { defaultPath }) => { defaultPath, }); }); + +ipc.handle('getScreenCaptureSources', async () => { + return desktopCapturer.getSources({ + fetchWindowIcons: true, + thumbnailSize: { height: 102, width: 184 }, + types: ['window', 'screen'], + }); +}); + +if (isTestEnvironment(getEnvironment())) { + ipc.handle('ci:test-electron:done', async (_event, info) => { + process.stdout.write( + `ci:test-electron:done=${JSON.stringify(info)}\n`, + () => app.quit() + ); + }); +} diff --git a/app/user_config.ts b/app/user_config.ts index b1ffddd8a..8b8f5124c 100644 --- a/app/user_config.ts +++ b/app/user_config.ts @@ -2,19 +2,29 @@ // SPDX-License-Identifier: AGPL-3.0-only import { join } from 'path'; +import { mkdirSync } from 'fs'; import { app } from 'electron'; import { start } from './base_config'; import config from './config'; +let userData: string | undefined; // Use separate data directory for benchmarks & development if (config.has('storagePath')) { - app.setPath('userData', String(config.get('storagePath'))); + userData = String(config.get('storagePath')); } else if (config.has('storageProfile')) { - const userData = join( + userData = join( app.getPath('appData'), `Signal-${config.get('storageProfile')}` ); +} + +if (userData !== undefined) { + try { + mkdirSync(userData, { recursive: true }); + } catch (error) { + console.error('Failed to create userData', error?.stack || String(error)); + } app.setPath('userData', userData); } diff --git a/libtextsecure/test/.eslintrc.js b/libtextsecure/test/.eslintrc.js deleted file mode 100644 index de9f8080f..000000000 --- a/libtextsecure/test/.eslintrc.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2020 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -module.exports = { - env: { - browser: true, - node: false, - mocha: true, - }, - parserOptions: { - sourceType: 'script', - }, - rules: { - strict: 'off', - 'more/no-then': 'off', - }, - globals: { - assert: true, - getString: true, - }, -}; diff --git a/libtextsecure/test/_test.js b/libtextsecure/test/_test.js deleted file mode 100644 index 4952266f8..000000000 --- a/libtextsecure/test/_test.js +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015-2020 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -/* global chai */ - -mocha.setup('bdd'); -window.assert = chai.assert; - -const OriginalReporter = mocha._reporter; - -const SauceReporter = function Constructor(runner) { - const failedTests = []; - - runner.on('end', () => { - window.mochaResults = runner.stats; - window.mochaResults.reports = failedTests; - }); - - runner.on('fail', (test, err) => { - const flattenTitles = item => { - const titles = []; - while (item.parent.title) { - titles.push(item.parent.title); - // eslint-disable-next-line no-param-reassign - item = item.parent; - } - return titles.reverse(); - }; - failedTests.push({ - name: test.title, - result: false, - message: err.message, - stack: err.stack, - titles: flattenTitles(test), - }); - }); - - // eslint-disable-next-line no-new - new OriginalReporter(runner); -}; - -SauceReporter.prototype = OriginalReporter.prototype; - -mocha.reporter(SauceReporter); - -/* - * global helpers for tests - */ - -window.Whisper = window.Whisper || {}; -window.Whisper.events = { - on() {}, - trigger() {}, -}; - -before(async () => { - try { - window.SignalContext.log.info('Initializing SQL in renderer'); - const isTesting = true; - await window.Signal.Data.startInRenderer(isTesting); - window.SignalContext.log.info('SQL initialized in renderer'); - } catch (err) { - window.SignalContext.log.error( - 'SQL failed to initialize', - err && err.stack ? err.stack : err - ); - } - - await window.Signal.Util.initializeMessageCounter(); -}); diff --git a/libtextsecure/test/fake_web_api.js b/libtextsecure/test/fake_web_api.js deleted file mode 100644 index 5bb4da454..000000000 --- a/libtextsecure/test/fake_web_api.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018-2020 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -window.setImmediate = window.nodeSetImmediate; - -const getKeysForIdentifierMap = {}; -const messagesSentMap = {}; - -const fakeCall = () => Promise.resolve(); - -const fakeAPI = { - confirmCode: fakeCall, - getAttachment: fakeCall, - getAvatar: fakeCall, - getDevices: fakeCall, - // getKeysForIdentifier : fakeCall, - getMyKeys: fakeCall, - getProfile: fakeCall, - getProvisioningResource: fakeCall, - putAttachment: fakeCall, - registerKeys: fakeCall, - requestVerificationSMS: fakeCall, - requestVerificationVoice: fakeCall, - // sendMessages: fakeCall, - setSignedPreKey: fakeCall, - - getKeysForIdentifier(number) { - const res = getKeysForIdentifierMap[number]; - if (res !== undefined) { - delete getKeysForIdentifierMap[number]; - return Promise.resolve(res); - } - throw new Error('getKeysForIdentfier of unknown/used number'); - }, - - sendMessages(destination, messageArray) { - for (let i = 0, max = messageArray.length; i < max; i += 1) { - const msg = messageArray[i]; - if ( - (msg.type !== 1 && msg.type !== 3) || - msg.destinationDeviceId === undefined || - msg.destinationRegistrationId === undefined || - msg.body === undefined || - msg.timestamp === undefined || - msg.relay !== undefined || - msg.destination !== undefined - ) { - throw new Error('Invalid message'); - } - - messagesSentMap[`${destination}.${messageArray[i].destinationDeviceId}`] = - msg; - } - }, -}; - -window.WebAPI = { - connect: () => fakeAPI, -}; diff --git a/libtextsecure/test/index.html b/libtextsecure/test/index.html deleted file mode 100644 index e4b831ce4..000000000 --- a/libtextsecure/test/index.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - -
- -