[MM-23067] Browser View (#1514)

* Browser-view: initial architectural changes + webpack (#1358)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* CR changes

* Browserview settings window (#1362)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* [BrowserView] renderer (#1378)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* make eslint be aware of webpack aliases

* some extra disable lines

* move badge management to main

* remove unneded import

* fixing errors

* wip

* back to having tabs

* switch tab working

* wip

* wip

* wip

* fix quitting error

* back to a working config

* configure retries

* add darkmode

* wip

* add error/loading screens

* fix settings while removing remote usage

* wip

* fix lint, get preload to load

* remove unused import

* remove log statements

* Bv menus (#1387)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* make eslint be aware of webpack aliases

* some extra disable lines

* move badge management to main

* remove unneded import

* fixing errors

* wip

* back to having tabs

* switch tab working

* wip

* wip

* wip

* fix quitting error

* back to a working config

* configure retries

* add darkmode

* wip

* add error/loading screens

* fix settings while removing remote usage

* wip

* fix lint, get preload to load

* remove unused import

* wip

* menus initially working as they should

* update deps, show context menu

* wip

* wip

* wip

* fix forward/back menu

* fix server menu

* allow navigating to external urls in the browser

* add defaults to menu

* fix logic

* set default options

* remove logs

* wip

* package.json

* fix merge results

* fix package-lock

* remove debug statements

* address CR requests

* [MM-22691][Browserview] fix tray icon (#1403)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* make eslint be aware of webpack aliases

* some extra disable lines

* move badge management to main

* remove unneded import

* fixing errors

* wip

* back to having tabs

* switch tab working

* wip

* wip

* wip

* fix quitting error

* back to a working config

* configure retries

* add darkmode

* wip

* add error/loading screens

* fix settings while removing remote usage

* wip

* fix lint, get preload to load

* remove unused import

* wip

* menus initially working as they should

* update deps, show context menu

* wip

* wip

* wip

* fix forward/back menu

* fix server menu

* allow navigating to external urls in the browser

* add defaults to menu

* fix logic

* set default options

* remove logs

* wip

* fix webpack adding images to /dist so tray can render them

* wait for config, fix menutray calls

* remove .gitattributes from being tracked

* remove unused reject

* remove logs

* Update webpack.config.renderer.js

Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com>

Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com>

* Browserview URLHover (#1393)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* make eslint be aware of webpack aliases

* some extra disable lines

* move badge management to main

* remove unneded import

* fixing errors

* wip

* back to having tabs

* switch tab working

* wip

* wip

* wip

* fix quitting error

* back to a working config

* configure retries

* add darkmode

* wip

* add error/loading screens

* fix settings while removing remote usage

* wip

* fix lint, get preload to load

* remove unused import

* wip

* menus initially working as they should

* update deps, show context menu

* wip

* wip

* wip

* fix forward/back menu

* fix server menu

* allow navigating to external urls in the browser

* add defaults to menu

* fix logic

* set default options

* remove logs

* wip

* wip

* wip urlview

* wip

* urlview when hovering on a link

* change how to detect when the mouse hovers

* [BrowserView] remove remote usage, fix menus and window buttons in Win (#1418)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* make eslint be aware of webpack aliases

* some extra disable lines

* move badge management to main

* remove unneded import

* fixing errors

* wip

* back to having tabs

* switch tab working

* wip

* wip

* wip

* fix quitting error

* back to a working config

* configure retries

* add darkmode

* wip

* add error/loading screens

* fix settings while removing remote usage

* wip

* fix lint, get preload to load

* remove unused import

* wip

* menus initially working as they should

* update deps, show context menu

* wip

* wip

* wip

* fix forward/back menu

* fix server menu

* allow navigating to external urls in the browser

* add defaults to menu

* fix logic

* set default options

* remove logs

* wip

* fix webpack adding images to /dist so tray can render them

* wait for config, fix menutray calls

* remove .gitattributes from being tracked

* remove unused reject

* remove logs

* Update webpack.config.renderer.js

Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com>

* fix three dot menu

* remove most remote usage, fix window buttons in Windows

Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com>

* fix different errors when loading config (#1420)

* [BrowserView] Native modules & registry access (#1417)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* make eslint be aware of webpack aliases

* some extra disable lines

* move badge management to main

* remove unneded import

* fixing errors

* wip

* back to having tabs

* switch tab working

* wip

* wip

* wip

* fix quitting error

* back to a working config

* configure retries

* add darkmode

* wip

* add error/loading screens

* fix settings while removing remote usage

* wip

* fix lint, get preload to load

* remove unused import

* wip

* menus initially working as they should

* update deps, show context menu

* wip

* wip

* wip

* fix forward/back menu

* fix server menu

* allow navigating to external urls in the browser

* add defaults to menu

* fix logic

* set default options

* remove logs

* wip

* fix webpack adding images to /dist so tray can render them

* wait for config, fix menutray calls

* remove .gitattributes from being tracked

* restart-working native modules

* setup env variables for installing native modules

* [browserview] Electron notifications (#1411)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* make eslint be aware of webpack aliases

* some extra disable lines

* move badge management to main

* remove unneded import

* fixing errors

* wip

* back to having tabs

* switch tab working

* wip

* wip

* wip

* fix quitting error

* back to a working config

* configure retries

* add darkmode

* wip

* add error/loading screens

* fix settings while removing remote usage

* wip

* fix lint, get preload to load

* remove unused import

* wip

* menus initially working as they should

* update deps, show context menu

* wip

* wip

* wip

* fix forward/back menu

* fix server menu

* allow navigating to external urls in the browser

* add defaults to menu

* fix logic

* set default options

* remove logs

* wip

* wip

* move viewmanager into windowmanager

* working notifications

* remove logs, switch tab on notification click

* download notifications

* fix tray

* fix menu switch server

* fix error

* [MM-23078] TabBar fixes for BrowserView (#1423)

* [MM-23078] TabBar fixes for BrowserView

* Removing unnecessary logging

* [Browserview] 4.6 and 4.7 PRs (#1424)

* [MM-28620] allow navigating links to admin_console #1374

* [MM-25789] - Update default settings for new installations #1376

* [MM-27332] show window at autolaunch #1379

* Update NOTICE.txt (#1385)

* Update NOTICE.txt

* Update NOTICE.txt

* Update NOTICE.txt

* convert to markdown

* md linting

* Update NOTICE.md

* Revert "Update NOTICE.md"

This reverts commit 9381fca895c0677bcad1cf1c1071ca88afd6f486.

* Revert "md linting"

This reverts commit e7a68f120109d47b9849cf816d4fef79483ad22f.

* Revert "convert to markdown"

This reverts commit 1e7ed8a67c9c98cd0d0f3ff6cdc70782effb143d.

* add missing licenses to joi and jq

* Remove devDependencies

Co-authored-by: Guillermo Vaya <guivaya@gmail.com>

* Notification sounds, also added tab name to notification title

* [MM-22013] - Allow users to specify default download locations #1383

* [MM-21835] Use URL instead of the url library #1384

* remove debug console.log statements

Co-authored-by: Amy Blais <amy_blais@hotmail.com>

* [MM-31266] fix access url when it's not a mm server (#1431)

* [MM-31224] fix reloading servers and other tab issues (#1434)

* [MM-31224] fix reloading servers and other tab issues

* reload if url changes

* Change the dev server port to 9001 to avoid conflict with mattermost-minio (#1437)

* remove dev_web_server (#1438)

* [MM-31225][MM-31217][MM-31219][Browserview] fix linux compilation + other fixes (#1433)

* fix linux errors

* remove registry, remove env_vars

* devtools in separate window, prevent config errors

* fix registry path

* move dist to root when packaging

* make devtools dettached to avoid browserview

* remove unneeded comment

* use reject in case of registry failure

* fix handling results

* fix application menu

* make linter happy

* fix missing key on apt-get (#1440) (#1442)

see https://github.com/electron-userland/electron-builder/issues/5485#issuecomment-749244332

* [MM-31221][BrowserView] first modal: adding a server while in a server view (#1400)

* reorder code to support webpack

* start backend changes

* remove simple-spellchecker

* wip

* first browserview run

* settings window routing

* wip

* back to webpack

* working build

* back to using electron-builder

* fix linting

* linting errors missed

* back to just 1 config

* missing changes

* refactor and have the settings in its own page

* reminder to restore disabling window.eval

* wip

* wip

* remove old webpack generated files

* add assets files

* more remove files and fix localurls

* wip settings, needs fixing saving prefs

* remove linting errors

* remove settings as a modal

* fix linting

* remove view from window on destroy

* restore visibility if reloaded

* debug log

* look for closed windows, remove managers from settings as it is a full window

* restore view on configuration save

* linting and debug

* remove debug message

* make eslint be aware of webpack aliases

* some extra disable lines

* move badge management to main

* remove unneded import

* fixing errors

* wip

* back to having tabs

* switch tab working

* wip

* wip

* wip

* fix quitting error

* back to a working config

* configure retries

* add darkmode

* wip

* add error/loading screens

* fix settings while removing remote usage

* wip

* fix lint, get preload to load

* remove unused import

* wip

* menus initially working as they should

* update deps, show context menu

* wip

* wip

* wip

* fix forward/back menu

* fix server menu

* allow navigating to external urls in the browser

* add defaults to menu

* fix logic

* set default options

* remove logs

* wip

* wip

* wip urlview

* wip

* urlview when hovering on a link

* wip

* wip

* first working modal

* fix config loading

* upgrade electron to 10.1.5

* esc exits modals

* first modal

* add env variables for settings and modals devtools

* adress CSS review comments

* Address review comments

* fix dist in prod

* fix preload path on build

* [MM-31987] Allow camera use for jitsi (#1443) (#1450)

* [MM-31987] allow camera use for jitsi

* update message for access

* [MM-31261] Use manual resizing of BrowserViews on resize, maximize and full-screen (#1449)

* [MM-31261] Use manual resizing of BrowserViews on resize, maximize and full-screen

* Update src/main/windows/windowManager.js

Co-authored-by: Guillermo Vayá <guivaya@gmail.com>

Co-authored-by: Guillermo Vayá <guivaya@gmail.com>

* add own branch for testing (#1448)

* add own branch for testing

* remove signing for windows

* add message to channel

* Bv pipeline elisabeth (#1452)

* Add parameter and remove schedule

* Add jq

* Fix adding jq

* Fix adding jq

* Fix adding jq

* fix quotes

* upload as JSON

* use previous, parse json

* fixes

* use json

Co-authored-by: Elisabeth Kulzer <elikul@elikul.de>

* [MM-30144][MM-30145][MM-30146][MM-30147] Migrate auth and certificate modals to BrowserView (#1445)

* WIP

* WIP

* WIP

* WIP

* WIP

* [MM-30144][MM-30145] Migrate LoginModal and PermissionModal to BrowserView

* [MM-30146][MM-30147] Migrate certificate modals to BrowserView

* Fixed transparency on the bootstrap modals

* PR feedback

* Added better error reporting in case the modal promise fails

* [MM-31233] Reverse maximize logic typo (#1454)

* [browser view] MM-32277 bump version, exe, cache errrors (#1456)

* bump version

* enable msi and remove src/package*

* ensure variable exists

* remove cleanCache script

* default expansion for env variable

* add commit version, missing package-lock.json

* remove duplicated command

* [MM-31467] Move protocol handling over from original MattermostView into web contents handler (#1453)

* WIP

* WIP

* [MM-31467] Move protocol handling over from original MattermostView into web contents handler

* Remove log statement

* [MM-32392] prevent crash when checking a URL (#1457)

* [MM-31215][MM-31387] Fixes for bad tab navigation and dragging (#1461)

* [MM-31387] Send to renderer on clicking server from settings window

* Use different event name for sending switch server info to renderer

* Have the viewManager let the renderer know when the tab has changed

* Couple more fixes around tabs

* Simplify URL compare logic

* [MM-31650] Restore focus to active server on modal and settings window closure + other fixes (#1455)

* [MM-31650] Focus active server on settings window and modal closure

* Disable tabs when a modal is open

* Revert to using original NewTeamModal component

* fix resize (#1462)

* [MM-32424] fix server devtools being hidden by browserview (#1459)

* [MM-32424] fix server devtools being hidden by browserview

* reverse logic

* [MM-20227][MM-31388] move to roles and fix focus (#1463)

* [MM-31570] update mentions/unreads/session on jewel, tray and dock (#1460)

* [MM-32333] Open public links in the user's default browser (#1468)

* [MM-32333] Open public links in the user's default browser

* Removed commented code

* [MM-31232] fix urlview present with no content (#1467)

* [MM-31343] Migrate Finder to BrowserView (#1466)

* WIP

* WIP

* WIP

* [MM-31343] Migrate Finder to BrowserView

* PR feedback

* Removing reference to this in non-class file

* use electron to handle spellchecking (#1469)

* [MM-32382] Use resize event instead of will-resize for monitoring size of BV (#1470)

* [MM-32570] Use OpenSans as the font for the URL preview modal (#1471)

* [MM-32570] Use OpenSans as the font for the URL preview modal

* Don't use bootstrap

* Fix draw badge (#1477)

* use canvas from window

* fix errors

* fix errors

* safer code injection

* [MM-31554] Add listener for config synchronization on the settings window (#1473)

* [MM-31554] Add listener for config synchronization on the settings window

* Synchronize the config if updated from outside the settings window

* [MM-28541] restore deeplinking (#1475)

* handle deeplinking

* fix app handling deeplinking

* remove outdated comment

* address review comments

* MM-32765_prevent crash on checking unread state (#1479)

* MM-31383 make no the default when asking to add a protocol (#1481)

* [MM-31340] Resize browser view and show back button when on non-team URL (#1472)

* WIP

* [MM-31340] Resize browser view and show back button when on non-team URL

* Fixed issue where switching tabs and resizing hides the back button

* Add error checking around going back in history

* [MM-31399] Use webapp ESLint config in desktop app and resolve inconsistencies (#1482)

* Import webapp eslint and update packages

* FIrst pass with new ruleset

* Allow setState

* Fix rule for tests

* Comment out skippeed tests, removed some TODOs and fixed some warnings

* Remove errors from MainPage

* Use indenting profile from webapp

* Update editorconfig for new indenting

* Fix indenting for class properties

* Only disable no-console for renderer process and scripts

* Remove rule overrides and changes

* Fix merge issues

* PR feedback and fixed a bad merge

* [MM-25122] Use modded version of winreg that supports UTF-8 (#1488)

* fix appicon path resolution (#1484)

* [MM-33141] Fixed use of bad context in TeamList (#1487)

* [MM-33141] Fixed use of bad context in TeamList

* Refactor to pull the functions out

* Remove unnecessary props

* [MM-25355] Throttle notifications for Windows by channel id (#1486)

* [MM-25355] Throttle notifications for Windows

* Use teamId as well to key the notifications

* Merge'd

* Use Map instead of Set

* [MM-33050] move webcontent events out of main (#1489)

* wip

* wip

* fix webcontent events, move views to its own folder

* [MM-33238] Check for admin URL when toggling back bar (#1495)

* [MM-31342] fix "save image as" context menu crash (#1490)

* [MM-33231] update jewel on new mentions/when read (#1493)

* [MM-33231] update state properly for a purecomponent

* remove unneeded comment

* [MM-33032] Use `hidden` titleBarStyle value to fix macOS Catalina click issue (#1496)

* [MM-32809] Remove Toggle Dark Mode menu item for Windows, enable toggling on Linux (#1494)

* [MM-32809] Remove Toggle Dark Mode menu item for Windows

* Just check for !win32 and !darwin

* Enable correct dark mode functionality on non-macOS/non-Windows machines

* [MM-33334] Restore keyboard shortcuts for menu items moved to roles (#1499)

* [MM-33434] Upgrade to Electron v11, some other dependency upgrades (#1501)

* [MM-33434] Upgrade to Electron v11, some other dependency upgrades

* Missed a version change

* context menu fix

* Forgot to remove a log statement

* Added resized for redundancy and upgraded to spectron 13

* Don't need resized

* [MM-33542] Trigger finder cleanup on pressing close or Escape (#1502)

* [MM-33542] Clear the Finder selection when closing the finder

* Remove listener on close as well

* Run close() on escape as well

* [MM-33607] Remove old badge code, update unreads code (#1503)

* [MM-33607] Remove old badge code, update unreads code

* Fix 2 random lint errors

* [MM-33247] Have the app handle links to other teams as a deep link (#1498)

* [MM-33373] Trigger the smaller font for 99+ mentions (#1507)

* [MM-32805] Merge master, migrate LoadingScreen to BrowserView (#1504)

* [MM-467] Notification sounds (#1351)

* Custom sounds

* Trying new version

* Trying new version

* Some fixes

* Rollback version change

* Allow native sound

* Increase version

* Playing custom sounds :)

* Fix var name

* Fix

* Update src/browser/js/notification.js

Co-authored-by: Guillermo Vayá <guivaya@gmail.com>

* Update src/browser/js/notification.js

Co-authored-by: Guillermo Vayá <guivaya@gmail.com>

* Update src/browser/js/notification.js

Co-authored-by: Guillermo Vayá <guivaya@gmail.com>

* Several suggestions

* Update src/browser/js/notification.js

Co-authored-by: Guillermo Vayá <guivaya@gmail.com>

* Restore of version

Co-authored-by: Guillermo Vayá <guivaya@gmail.com>

* Clean caches on depcheck failure (#1369)

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>

* [MM-28595] Open team links within the app (#1373)

* [MM-25789] - Update default settings for new installations (#1376)

* [MM-25789] - Update default settings for new installations

* Update src/main.js

Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>

* Update src/main.js

Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>

* Fix linter

Co-authored-by: Nevyana Angelova <nevyangelova@Nevyanas-MacBook-Pro-2.local>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>

* add Russian language in the list available for spellcheck (#1375)

* [MM-28620] allow navigating links to admin_console (#1374)

* [MM-28620] allow navigating links to admin_console

* Fix when there is not a server associated

* [MM-27332] show window at autolaunch (#1379)

* Bump to version 4.7.0-develop

* Update NOTICE.txt (#1385)

* Update NOTICE.txt

* Update NOTICE.txt

* Update NOTICE.txt

* convert to markdown

* md linting

* Update NOTICE.md

* Revert "Update NOTICE.md"

This reverts commit 9381fca895c0677bcad1cf1c1071ca88afd6f486.

* Revert "md linting"

This reverts commit e7a68f120109d47b9849cf816d4fef79483ad22f.

* Revert "convert to markdown"

This reverts commit 1e7ed8a67c9c98cd0d0f3ff6cdc70782effb143d.

* add missing licenses to joi and jq

* Remove devDependencies

Co-authored-by: Guillermo Vaya <guivaya@gmail.com>

* [MM-9922] Hide tooltip for internal links (channels, timestamps, etc.) (#1386)

* Hide tooltip for internal links (channels, timestamps, etc.)

* Only hide tooltip for internal links on the *current* team

* feat(spellcheck): add Ukrainian language for spellcheck (#1382)

* [MM-29677] fix download complete notification not appearing (#1388)

* fix soundname not existing (#1390)

* [MM-29921] fix custom sound not playing when receiving a notification (#1396)

* [MM-29921] fix sound notification

* remove logs

* Update release-process.md (#1394)

* [MM-22013] - Allow users to specify default download locations (#1383)

* [MM-22013] - Allow users to specify default download locations

* PR comments

* Add proper config prop

* Update src/browser/components/SettingsPage.jsx

Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>

* Remove string ref

* Fix styling

* Update styling

* Disable input

* Add variable for windows

* Prevent dialog from opening twice

Co-authored-by: Nevyana Angelova <nevyangelova@Nevyanas-MBP-2.fritz.box>
Co-authored-by: Nevyana Angelova <nevyangelova@Nevyanas-MacBook-Pro-2.local>
Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>

* [MM-21835] Use URL instead of the url library (#1384)

Additionally, migrate all of the URL related helper functions
from `src/utils/utils.js` to the new `src/utils/url.js` file
and migrate tests.

Issue MM-21835
Fixes #1206

* Merge Powershell files together and remove AppVeyor related code

* Ensure nodejs deps are met before running script argument directly

* [MM-22810] Update loading screen with new design & animation (#1409)

* Update loading screen with new design & animation

* add prop back in

* adjust z-index for tests

* tweaks to pass tests

* address offline feedback

- shrink initial logo size
- introduce a slight delay before fading loading spinner out
- fix horizontal scrollbar showing on load screen

* add missing css variable

* no need to remove loading icon

* Apply suggestions from code review

Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>

* Move LoadingScreen.jsx to file-only component

* Rename prop for better clarity

* Default prop to none and check when needed

* Update import paths

* Add ESDocs and remove unecessary conditional

* Forgot to remove the eslint override

Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>

* [MM-22960] - Keep desktop app pinned to taskb bar when the app upgrades (#1397)

Co-authored-by: Nevyana Angelova <nevyangelova@Nevyanas-MacBook-Pro-2.local>

* Bump highlight.js from 9.18.1 to 9.18.5 (#1421)

Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 9.18.1 to 9.18.5.
- [Release notes](https://github.com/highlightjs/highlight.js/releases)
- [Changelog](https://github.com/highlightjs/highlight.js/blob/9.18.5/CHANGES.md)
- [Commits](https://github.com/highlightjs/highlight.js/compare/9.18.1...9.18.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump ini from 1.3.5 to 1.3.7 (#1427)

Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix missing key on apt-get (#1440)

see https://github.com/electron-userland/electron-builder/issues/5485#issuecomment-749244332

* [MM-31987] Allow camera use for jitsi (#1443)

* [MM-31987] allow camera use for jitsi

* update message for access

* Created codeql analysis (#1441)

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>

* [MM-31626] bypass gitlab browser-check for oauth login (#1439)

* MM-31626 make User Agent configurable by user

* add info

* remove chrome from UA for gitlab.com

* remove previous solution

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>

* Add Swedish sv-SE (already in simple-spellchecker) (#1483)

* Add Swedish sv-SE (already in simple-spellchecker)

* Remove spaces in empty lines

* Add some sv-SE test for spellchecker

Co-authored-by: Peter Johansson <peter.johansson@havochvatten.se>

* Add loading screen, fix reload

* WIP

* Migrate LoadingScreen to BrowserView

* Lint fixes

* Removed gitlab fix code, also returning null is bad apparently

* Fix reload logic

Co-authored-by: Rodrigo Villablanca <villa061004@gmail.com>
Co-authored-by: Guillermo Vayá <guivaya@gmail.com>
Co-authored-by: Juho Nurminen <juho.nurminen@mattermost.com>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>
Co-authored-by: Nev Angelova <nevy.angelova@gmail.com>
Co-authored-by: Nevyana Angelova <nevyangelova@Nevyanas-MacBook-Pro-2.local>
Co-authored-by: Eugeny Fomin <github.com@jeka.ru>
Co-authored-by: Amy Blais <amy_blais@hotmail.com>
Co-authored-by: Nathan Bolender <nathan@nathanbolender.com>
Co-authored-by: Dmitriy Danilov <daniloff200@gmail.com>
Co-authored-by: Nevyana Angelova <nevyangelova@Nevyanas-MBP-2.fritz.box>
Co-authored-by: FalseHonesty <skipboman0@gmail.com>
Co-authored-by: William Gathoye <william@gathoye.be>
Co-authored-by: Dean Whillier <deanwhillier@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rohitesh Gupta <srkg.gupta@gmail.com>
Co-authored-by: petermcj <petermcj@gmail.com>
Co-authored-by: Peter Johansson <peter.johansson@havochvatten.se>

* [MM-33668] Restore tests to browser-view branch (#1506)

* happy eslint

* wip

* wip

* remove aliases

* almost working tests

* green tests

* Revert "remove aliases"

This reverts commit 803d3695538197407b45e0d8d30dc429b259b7f3.

* add unit test, reconfigure package scripts, make test pass

* [MM-33542] Trigger finder cleanup on pressing close or Escape (#1502)

* [MM-33542] Clear the Finder selection when closing the finder

* Remove listener on close as well

* Run close() on escape as well

* [MM-33607] Remove old badge code, update unreads code (#1503)

* [MM-33607] Remove old badge code, update unreads code

* Fix 2 random lint errors

* fix script naming in circle

* fix check deps

* attempt to fix dependency-check download

* remove check-deps step

Co-authored-by: = <=>
Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com>

* Cleanup of BrowserView migration, some bug fixes (#1509)

* 1st round of cleanup

* 2nd round of cleanup

* Set constant for reload-config

* Cleaned up some TODOs

* store daily build to S3 (#1508)

* store daily build to S3

* missing colon

* fix paths

* try to keep folders

* remove unneeded step

* change from arn to bucket name

* keep organization consistent

* fix indentation

* fix indentation x2

Co-authored-by: = <=>

* MM-33551 keep tray state between themes (#1511)

Co-authored-by: = <=>

* Set to version v4.7

Co-authored-by: Guillermo Vayá <guillermo.vaya@mattermost.com>
Co-authored-by: Amy Blais <amy_blais@hotmail.com>
Co-authored-by: Guillermo Vayá <guivaya@gmail.com>
Co-authored-by: Elisabeth Kulzer <elikul@elikul.de>
Co-authored-by: Rodrigo Villablanca <villa061004@gmail.com>
Co-authored-by: Juho Nurminen <juho.nurminen@mattermost.com>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Nev Angelova <nevy.angelova@gmail.com>
Co-authored-by: Nevyana Angelova <nevyangelova@Nevyanas-MacBook-Pro-2.local>
Co-authored-by: Eugeny Fomin <github.com@jeka.ru>
Co-authored-by: Nathan Bolender <nathan@nathanbolender.com>
Co-authored-by: Dmitriy Danilov <daniloff200@gmail.com>
Co-authored-by: Nevyana Angelova <nevyangelova@Nevyanas-MBP-2.fritz.box>
Co-authored-by: FalseHonesty <skipboman0@gmail.com>
Co-authored-by: William Gathoye <william@gathoye.be>
Co-authored-by: Dean Whillier <deanwhillier@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rohitesh Gupta <srkg.gupta@gmail.com>
Co-authored-by: petermcj <petermcj@gmail.com>
Co-authored-by: Peter Johansson <peter.johansson@havochvatten.se>
This commit is contained in:
Devin Binnie
2021-03-18 10:51:53 -04:00
committed by GitHub
parent 9551d6628c
commit e76e0dc0a1
209 changed files with 17905 additions and 13921 deletions

View File

@@ -4,19 +4,23 @@
'use strict';
const fs = require('fs');
const path = require('path');
const Application = require('spectron').Application;
const chai = require('chai');
const {ipcRenderer} = require('electron');
const {SHOW_SETTINGS_WINDOW} = require('../../src/common/communication');
chai.should();
const sourceRootDir = path.join(__dirname, '../..');
const electronBinaryPath = (() => {
if (process.platform === 'darwin') {
return path.join(sourceRootDir, 'node_modules/electron/dist/Electron.app/Contents/MacOS/Electron');
}
const exeExtension = (process.platform === 'win32') ? '.exe' : '';
return path.join(sourceRootDir, 'node_modules/electron/dist/electron' + exeExtension);
if (process.platform === 'darwin') {
return path.join(sourceRootDir, 'node_modules/electron/dist/Electron.app/Contents/MacOS/Electron');
}
const exeExtension = (process.platform === 'win32') ? '.exe' : '';
return path.join(sourceRootDir, 'node_modules/electron/dist/electron' + exeExtension);
})();
const userDataDir = path.join(sourceRootDir, 'test/testUserData/');
const configFilePath = path.join(userDataDir, 'config.json');
@@ -24,79 +28,81 @@ const boundsInfoPath = path.join(userDataDir, 'bounds-info.json');
const mattermostURL = 'http://example.com/';
module.exports = {
sourceRootDir,
configFilePath,
userDataDir,
boundsInfoPath,
mattermostURL,
sourceRootDir,
configFilePath,
userDataDir,
boundsInfoPath,
mattermostURL,
cleanTestConfig() {
[configFilePath, boundsInfoPath].forEach((file) => {
try {
fs.unlinkSync(file);
} catch (err) {
if (err.code !== 'ENOENT') {
console.error(err);
cleanTestConfig() {
[configFilePath, boundsInfoPath].forEach((file) => {
try {
fs.unlinkSync(file);
} catch (err) {
if (err.code !== 'ENOENT') {
// eslint-disable-next-line no-console
console.error(err);
}
}
});
},
createTestUserDataDir() {
if (!fs.existsSync(userDataDir)) {
fs.mkdirSync(userDataDir);
}
}
});
},
},
createTestUserDataDir() {
if (!fs.existsSync(userDataDir)) {
fs.mkdirSync(userDataDir);
}
},
getSpectronApp() {
const options = {
path: electronBinaryPath,
args: [`${path.join(sourceRootDir, 'src')}`, `--data-dir=${userDataDir}`, '--disable-dev-mode'],
chromeDriverArgs: [],
// enable this if chromedriver hangs to see logs
// chromeDriverLogPath: '../chromedriverlog.txt',
};
if (process.platform === 'darwin' || process.platform === 'linux') {
// on a mac, debbuging port might conflict with other apps
// this changes the default debugging port so chromedriver can run without issues.
options.chromeDriverArgs.push('remote-debugging-port=9222');
}
return new Application(options);
},
addClientCommands(client) {
client.addCommand('loadSettingsPage', function async() {
return this.url('file://' + path.join(sourceRootDir, 'src/browser/settings.html')).waitUntilWindowLoaded();
});
client.addCommand('isNodeEnabled', function async() {
return this.execute(() => {
try {
if (require('child_process')) {
return true;
}
return false;
} catch (e) {
return false;
getSpectronApp() {
const options = {
path: electronBinaryPath,
args: [`${path.join(sourceRootDir, 'dist')}`, `--data-dir=${userDataDir}`, '--disable-dev-mode'],
chromeDriverArgs: [],
};
if (process.env.MM_DEBUG_SETTINGS) {
options.chromeDriverLogPath = './chromedriverlog.txt';
}
}).then((requireResult) => {
return requireResult.value;
});
});
client.addCommand('waitForAppOptionsAutoSaved', function async() {
const ID_APP_OPTIONS_SAVE_INDICATOR = '#appOptionsSaveIndicator';
const TIMEOUT = 5000;
return this.
waitForVisible(ID_APP_OPTIONS_SAVE_INDICATOR, TIMEOUT).
waitForVisible(ID_APP_OPTIONS_SAVE_INDICATOR, TIMEOUT, true);
});
},
if (process.platform === 'darwin' || process.platform === 'linux') {
// on a mac, debbuging port might conflict with other apps
// this changes the default debugging port so chromedriver can run without issues.
options.chromeDriverArgs.push('remote-debugging-port=9222');
}
return new Application(options);
},
// execute the test only when `condition` is true
shouldTest(it, condition) {
return condition ? it : it.skip;
},
isOneOf(platforms) {
return (platforms.indexOf(process.platform) !== -1);
},
addClientCommands(client) {
client.addCommand('loadSettingsPage', function async() {
ipcRenderer.send(SHOW_SETTINGS_WINDOW);
});
client.addCommand('isNodeEnabled', function async() {
return this.execute(() => {
try {
if (require('child_process')) {
return true;
}
return false;
} catch (e) {
return false;
}
}).then((requireResult) => {
return requireResult.value;
});
});
client.addCommand('waitForAppOptionsAutoSaved', function async() {
const ID_APP_OPTIONS_SAVE_INDICATOR = '#appOptionsSaveIndicator';
const TIMEOUT = 5000;
return this.
waitForVisible(ID_APP_OPTIONS_SAVE_INDICATOR, TIMEOUT).
waitForVisible(ID_APP_OPTIONS_SAVE_INDICATOR, TIMEOUT, true);
});
},
// execute the test only when `condition` is true
shouldTest(it, condition) {
// eslint-disable-next-line no-only-tests/no-only-tests
return condition ? it : it.skip;
},
isOneOf(platforms) {
return (platforms.indexOf(process.platform) !== -1);
},
};

View File

@@ -3,13 +3,13 @@
// See LICENSE.txt for license information.
function asyncSleep(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
}
module.exports = {
asyncSleep,
asyncSleep,
};

View File

@@ -8,145 +8,144 @@ const fs = require('fs');
const env = require('../modules/environment');
describe('application', function desc() {
this.timeout(30000);
this.timeout(30000);
beforeEach(() => {
env.createTestUserDataDir();
env.cleanTestConfig();
this.app = env.getSpectronApp();
return this.app.start();
});
afterEach(async () => {
if (this.app && this.app.isRunning()) {
await this.app.stop();
}
});
it('should show a window', async () => {
await this.app.client.waitUntilWindowLoaded();
const count = await this.app.client.getWindowCount();
count.should.equal(1);
const opened = await this.app.browserWindow.isDevToolsOpened();
opened.should.be.false;
const visible = await this.app.browserWindow.isVisible();
visible.should.be.true;
});
if (process.platform === 'darwin') {
it.skip('should show closed window with cmd+tab', async () => {
// Unable to utilize Command key press due to: https://bugs.chromium.org/p/chromedriver/issues/detail?id=3023#c2
await this.app.client.waitUntilWindowLoaded();
await this.app.client.keys(['Meta', 'w']);
let visible = await this.app.browserWindow.isVisible();
visible.should.be.false;
this.app.client.keys(['Meta', 'Tab']);
visible = await this.app.browserWindow.isVisible();
visible.should.be.true;
beforeEach(() => {
env.createTestUserDataDir();
env.cleanTestConfig();
this.app = env.getSpectronApp();
return this.app.start();
});
}
it.skip('should restore window bounds', async () => {
// bounds seems to be incorrectly calculated in some environments
// - Windows 10: OK
// - CircleCI: NG
const expectedBounds = {x: 100, y: 200, width: 300, height: 400};
fs.writeFileSync(env.boundsInfoPath, JSON.stringify(expectedBounds));
await this.app.restart();
const bounds = await this.app.browserWindow.getBounds();
bounds.should.deep.equal(expectedBounds);
});
afterEach(async () => {
if (this.app && this.app.isRunning()) {
await this.app.stop();
}
});
it('should NOT restore window bounds if the origin is located on outside of viewarea', async () => {
it('should show two windows if there is no config file', async () => {
await this.app.client.waitUntilWindowLoaded();
const count = await this.app.client.getWindowCount();
count.should.equal(2);
const opened = await this.app.browserWindow.isDevToolsOpened();
opened.should.be.false;
const visible = await this.app.browserWindow.isVisible();
visible.should.be.true;
});
if (process.platform === 'darwin') {
it.skip('should show closed window with cmd+tab', async () => {
// Unable to utilize Command key press due to: https://bugs.chromium.org/p/chromedriver/issues/detail?id=3023#c2
await this.app.client.waitUntilWindowLoaded();
await this.app.client.keys(['Meta', 'w']);
let visible = await this.app.browserWindow.isVisible();
visible.should.be.false;
this.app.client.keys(['Meta', 'Tab']);
visible = await this.app.browserWindow.isVisible();
visible.should.be.true;
});
}
it.skip('should restore window bounds', async () => {
// bounds seems to be incorrectly calculated in some environments
// - Windows 10: OK
// - CircleCI: NG
const expectedBounds = {x: 100, y: 200, width: 300, height: 400};
fs.writeFileSync(env.boundsInfoPath, JSON.stringify(expectedBounds));
await this.app.restart();
const bounds = await this.app.browserWindow.getBounds();
bounds.should.deep.equal(expectedBounds);
});
it('should NOT restore window bounds if the origin is located on outside of viewarea', async () => {
// bounds seems to be incorrectly calculated in some environments (e.g. CircleCI)
// - Windows 10: OK
// - CircleCI: NG
fs.writeFileSync(env.boundsInfoPath, JSON.stringify({x: -100000, y: 200, width: 300, height: 400}));
await this.app.restart();
let bounds = await this.app.browserWindow.getBounds();
bounds.x.should.satisfy((x) => (x > -10000));
fs.writeFileSync(env.boundsInfoPath, JSON.stringify({x: -100000, y: 200, width: 300, height: 400}));
await this.app.restart();
let bounds = await this.app.browserWindow.getBounds();
bounds.x.should.satisfy((x) => (x > -10000));
fs.writeFileSync(env.boundsInfoPath, JSON.stringify({x: 100, y: 200000, width: 300, height: 400}));
await this.app.restart();
bounds = await this.app.browserWindow.getBounds();
bounds.y.should.satisfy((y) => (y < 10000));
});
it('should show settings.html when there is no config file', async () => {
await this.app.client.waitUntilWindowLoaded();
await this.app.client.pause(1000);
const url = await this.app.client.getUrl();
url.should.match(/\/settings.html$/);
const existing = await this.app.client.isExisting('#newServerModal');
existing.should.equal(true);
});
it('should show index.html when there is config file', async () => {
const config = {
version: 2,
teams: [{
name: 'example',
url: env.mattermostURL,
order: 0,
}, {
name: 'github',
url: 'https://github.com/',
order: 1,
}],
showTrayIcon: false,
trayIconTheme: 'light',
minimizeToTray: false,
notifications: {
flashWindow: 0,
bounceIcon: false,
bounceIconType: 'informational',
},
showUnreadBadge: true,
useSpellChecker: true,
enableHardwareAcceleration: true,
autostart: true,
darkMode: false,
};
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
await this.app.restart();
const url = await this.app.client.getUrl();
url.should.match(/\/index.html$/);
});
it('should upgrade v0 config file', async () => {
const Config = require('../../src/common/config').default;
const newConfig = new Config(env.configFilePath);
const oldConfig = {
url: env.mattermostURL,
};
fs.writeFileSync(env.configFilePath, JSON.stringify(oldConfig));
await this.app.restart();
const url = await this.app.client.getUrl();
url.should.match(/\/index.html$/);
const str = fs.readFileSync(env.configFilePath, 'utf8');
const upgradedConfig = JSON.parse(str);
upgradedConfig.version.should.equal(newConfig.defaultData.version);
});
it.skip('should be stopped when the app instance already exists', (done) => {
const secondApp = env.getSpectronApp();
// In the correct case, 'start().then' is not called.
// So need to use setTimeout in order to finish this test.
const timer = setTimeout(() => {
done();
}, 3000);
secondApp.start().then(() => {
clearTimeout(timer);
return secondApp.stop();
}).then(() => {
done(new Error('Second app instance exists'));
fs.writeFileSync(env.boundsInfoPath, JSON.stringify({x: 100, y: 200000, width: 300, height: 400}));
await this.app.restart();
bounds = await this.app.browserWindow.getBounds();
bounds.y.should.satisfy((y) => (y < 10000));
});
});
// it('should show settings.html when there is no config file', async () => {
// await this.app.client.waitUntilWindowLoaded();
// await this.app.client.pause(1000);
// const url = await this.app.client.getUrl();
// url.should.match(/\/settings.html$/);
// const existing = await this.app.client.isExisting('#newServerModal');
// existing.should.equal(true);
// });
// it('should show index.html when there is config file', async () => {
// const config = {
// version: 2,
// teams: [{
// name: 'example',
// url: env.mattermostURL,
// order: 0,
// }, {
// name: 'github',
// url: 'https://github.com/',
// order: 1,
// }],
// showTrayIcon: false,
// trayIconTheme: 'light',
// minimizeToTray: false,
// notifications: {
// flashWindow: 0,
// bounceIcon: false,
// bounceIconType: 'informational',
// },
// showUnreadBadge: true,
// useSpellChecker: true,
// enableHardwareAcceleration: true,
// autostart: true,
// darkMode: false,
// };
// fs.writeFileSync(env.configFilePath, JSON.stringify(config));
// await this.app.restart();
// const url = await this.app.client.getUrl();
// url.should.match(/\/index.html$/);
// });
// it('should upgrade v0 config file', async () => {
// const Config = require('../../src/common/config').default;
// const newConfig = new Config(env.configFilePath);
// const oldConfig = {
// url: env.mattermostURL,
// };
// fs.writeFileSync(env.configFilePath, JSON.stringify(oldConfig));
// await this.app.restart();
// const url = await this.app.client.getUrl();
// url.should.match(/\/index.html$/);
// const str = fs.readFileSync(env.configFilePath, 'utf8');
// const upgradedConfig = JSON.parse(str);
// upgradedConfig.version.should.equal(newConfig.defaultData.version);
// });
// it.skip('should be stopped when the app instance already exists', (done) => {
// const secondApp = env.getSpectronApp();
// // In the correct case, 'start().then' is not called.
// // So need to use setTimeout in order to finish this test.
// const timer = setTimeout(() => {
// done();
// }, 3000);
// secondApp.start().then(() => {
// clearTimeout(timer);
// return secondApp.stop();
// }).then(() => {
// done(new Error('Second app instance exists'));
// });
});

View File

@@ -4,211 +4,212 @@
'use strict';
const fs = require('fs');
const http = require('http');
const path = require('path');
const env = require('../../modules/environment');
const {asyncSleep} = require('../../modules/utils');
describe('browser/index.html', function desc() {
this.timeout(30000);
const config = {
version: 2,
teams: [{
name: 'example',
url: env.mattermostURL,
order: 0,
}, {
name: 'github',
url: 'https://github.com/',
order: 1,
}],
showTrayIcon: false,
trayIconTheme: 'light',
minimizeToTray: false,
notifications: {
flashWindow: 0,
bounceIcon: false,
bounceIconType: 'informational',
},
showUnreadBadge: true,
useSpellChecker: true,
enableHardwareAcceleration: true,
autostart: true,
darkMode: false,
};
const serverPort = 8181;
before(() => {
function serverCallback(req, res) {
res.writeHead(200, {
'Content-Type': 'text/html',
});
res.end(fs.readFileSync(path.resolve(env.sourceRootDir, 'test/modules/test.html'), 'utf-8'));
}
this.server = http.createServer(serverCallback).listen(serverPort, '127.0.0.1');
});
beforeEach(async () => {
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
await asyncSleep(1000);
this.app = env.getSpectronApp();
await this.app.start();
});
afterEach(async () => {
if (this.app && this.app.isRunning()) {
await this.app.stop();
}
});
after((done) => {
this.server.close(done);
});
it('should set src of webview from config file', async () => {
const src0 = await this.app.client.getAttribute('#mattermostView0', 'src');
src0.should.equal(config.teams[0].url);
const src1 = await this.app.client.getAttribute('#mattermostView1', 'src');
src1.should.equal(config.teams[1].url);
const existing = await this.app.client.isExisting('#mattermostView2');
existing.should.be.false;
});
it('should set name of tab from config file', async () => {
const tabName0 = await this.app.client.getText('#teamTabItem0');
tabName0.should.equal(config.teams[0].name);
const tabName1 = await this.app.client.getText('#teamTabItem1');
tabName1.should.equal(config.teams[1].name);
});
it('should show only the selected team', () => {
return this.app.client.
waitForVisible('#mattermostView0', 2000).
waitForVisible('#mattermostView1', 2000, true).
click('#teamTabItem1').
waitForVisible('#mattermostView1', 2000).
waitForVisible('#mattermostView0', 2000, true);
});
// validation now prevents incorrect url's from being used
it.skip('should show error when using incorrect URL', async () => {
describe('renderer/index.html', function desc() {
this.timeout(30000);
fs.writeFileSync(env.configFilePath, JSON.stringify({
version: 2,
teams: [{
name: 'error_1',
url: 'http://false',
order: 0,
}],
}));
await this.app.restart();
return this.app.client.
waitForVisible('#mattermostView0-fail', 20000);
});
it('should set window title by using webview\'s one', async () => {
fs.writeFileSync(env.configFilePath, JSON.stringify({
version: 2,
teams: [{
name: 'title_test',
url: `http://localhost:${serverPort}`,
order: 0,
}],
}));
await this.app.restart();
await this.app.client.pause(2000);
const windowTitle = await this.app.browserWindow.getTitle();
windowTitle.should.equal('Mattermost Desktop testing html');
});
const config = {
version: 2,
teams: [{
name: 'example',
url: env.mattermostURL,
order: 0,
}, {
name: 'github',
url: 'https://github.com/',
order: 1,
}],
showTrayIcon: false,
trayIconTheme: 'light',
minimizeToTray: false,
notifications: {
flashWindow: 0,
bounceIcon: false,
bounceIconType: 'informational',
},
showUnreadBadge: true,
useSpellChecker: true,
enableHardwareAcceleration: true,
autostart: true,
darkMode: false,
};
// Skip because it's very unstable in CI
it.skip('should update window title when the activated tab\'s title is updated', async () => {
fs.writeFileSync(env.configFilePath, JSON.stringify({
version: 2,
teams: [{
name: 'title_test_0',
url: `http://localhost:${serverPort}`,
order: 0,
}, {
name: 'title_test_1',
url: `http://localhost:${serverPort}`,
order: 1,
}],
}));
await this.app.restart();
await this.app.client.pause(500);
const serverPort = 8181;
// Note: Indices of webview are correct.
// Somehow they are swapped.
await this.app.client.
windowByIndex(2).
execute(() => {
document.title = 'Title 0';
});
await this.app.client.windowByIndex(0).pause(500);
let windowTitle = await this.app.browserWindow.getTitle();
windowTitle.should.equal('Title 0');
before(() => {
function serverCallback(req, res) {
res.writeHead(200, {
'Content-Type': 'text/html',
});
res.end(fs.readFileSync(path.resolve(env.sourceRootDir, 'test/modules/test.html'), 'utf-8'));
}
this.server = http.createServer(serverCallback).listen(serverPort, '127.0.0.1');
});
await this.app.client.
windowByIndex(1).
execute(() => {
document.title = 'Title 1';
});
await this.app.client.windowByIndex(0).pause(500);
windowTitle = await this.app.browserWindow.getTitle();
windowTitle.should.equal('Title 0');
});
beforeEach(async () => {
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
await asyncSleep(1000);
this.app = env.getSpectronApp();
await this.app.start();
});
// Skip because it's very unstable in CI
it.skip('should update window title when a tab is selected', async () => {
fs.writeFileSync(env.configFilePath, JSON.stringify({
version: 2,
teams: [{
name: 'title_test_0',
url: `http://localhost:${serverPort}`,
order: 0,
}, {
name: 'title_test_1',
url: `http://localhost:${serverPort}`,
order: 1,
}],
}));
await this.app.restart();
afterEach(async () => {
if (this.app && this.app.isRunning()) {
await this.app.stop();
}
});
// Note: Indices of webview are correct.
// Somehow they are swapped.
await this.app.client.pause(500);
after((done) => {
this.server.close(done);
});
await this.app.client.
windowByIndex(2).
execute(() => {
document.title = 'Title 0';
});
await this.app.client.
windowByIndex(1).
execute(() => {
document.title = 'Title 1';
});
await this.app.client.windowByIndex(0).pause(500);
// it('should set src of webview from config file', async () => {
// const src0 = await this.app.client.getAttribute('#mattermostView0', 'src');
// src0.should.equal(config.teams[0].url);
let windowTitle = await this.app.browserWindow.getTitle();
windowTitle.should.equal('Title 0');
// const src1 = await this.app.client.getAttribute('#mattermostView1', 'src');
// src1.should.equal(config.teams[1].url);
await this.app.client.click('#teamTabItem1').pause(500);
windowTitle = await this.app.browserWindow.getTitle();
windowTitle.should.equal('Title 1');
});
// const existing = await this.app.client.isExisting('#mattermostView2');
// existing.should.be.false;
// });
it('should open the new server prompt after clicking the add button', async () => {
// See settings_test for specs that cover the actual prompt
await this.app.client.click('#addServerButton').pause(500);
const isModalExisting = await this.app.client.isExisting('#newServerModal');
isModalExisting.should.be.true;
});
// it('should set name of tab from config file', async () => {
// const tabName0 = await this.app.client.getText('#teamTabItem0');
// tabName0.should.equal(config.teams[0].name);
// const tabName1 = await this.app.client.getText('#teamTabItem1');
// tabName1.should.equal(config.teams[1].name);
// });
// it('should show only the selected team', () => {
// return this.app.client.
// waitForVisible('#mattermostView0', 2000).
// waitForVisible('#mattermostView1', 2000, true).
// click('#teamTabItem1').
// waitForVisible('#mattermostView1', 2000).
// waitForVisible('#mattermostView0', 2000, true);
// });
// validation now prevents incorrect url's from being used
// it.skip('should show error when using incorrect URL', async () => {
// this.timeout(30000);
// fs.writeFileSync(env.configFilePath, JSON.stringify({
// version: 2,
// teams: [{
// name: 'error_1',
// url: 'http://false',
// order: 0,
// }],
// }));
// await this.app.restart();
// return this.app.client.
// waitForVisible('#mattermostView0-fail', 20000);
// });
it('shouldn\'t set window title by using webview\'s one', async () => {
fs.writeFileSync(env.configFilePath, JSON.stringify({
version: 2,
teams: [{
name: 'title_test',
url: `http://localhost:${serverPort}`,
order: 0,
}],
}));
await this.app.restart();
await this.app.client.pause(2000);
const windowTitle = await this.app.browserWindow.getTitle();
windowTitle.should.equal('Mattermost Desktop App');
});
// Skip because it's very unstable in CI
// it.skip('should update window title when the activated tab\'s title is updated', async () => {
// fs.writeFileSync(env.configFilePath, JSON.stringify({
// version: 2,
// teams: [{
// name: 'title_test_0',
// url: `http://localhost:${serverPort}`,
// order: 0,
// }, {
// name: 'title_test_1',
// url: `http://localhost:${serverPort}`,
// order: 1,
// }],
// }));
// await this.app.restart();
// await this.app.client.pause(500);
// // Note: Indices of webview are correct.
// // Somehow they are swapped.
// await this.app.client.
// windowByIndex(2).
// execute(() => {
// document.title = 'Title 0';
// });
// await this.app.client.windowByIndex(0).pause(500);
// let windowTitle = await this.app.browserWindow.getTitle();
// windowTitle.should.equal('Title 0');
// await this.app.client.
// windowByIndex(1).
// execute(() => {
// document.title = 'Title 1';
// });
// await this.app.client.windowByIndex(0).pause(500);
// windowTitle = await this.app.browserWindow.getTitle();
// windowTitle.should.equal('Title 0');
// });
// Skip because it's very unstable in CI
// it.skip('should update window title when a tab is selected', async () => {
// fs.writeFileSync(env.configFilePath, JSON.stringify({
// version: 2,
// teams: [{
// name: 'title_test_0',
// url: `http://localhost:${serverPort}`,
// order: 0,
// }, {
// name: 'title_test_1',
// url: `http://localhost:${serverPort}`,
// order: 1,
// }],
// }));
// await this.app.restart();
// // Note: Indices of webview are correct.
// // Somehow they are swapped.
// await this.app.client.pause(500);
// await this.app.client.
// windowByIndex(2).
// execute(() => {
// document.title = 'Title 0';
// });
// await this.app.client.
// windowByIndex(1).
// execute(() => {
// document.title = 'Title 1';
// });
// await this.app.client.windowByIndex(0).pause(500);
// let windowTitle = await this.app.browserWindow.getTitle();
// windowTitle.should.equal('Title 0');
// await this.app.client.click('#teamTabItem1').pause(500);
// windowTitle = await this.app.browserWindow.getTitle();
// windowTitle.should.equal('Title 1');
// });
// it('should open the new server prompt after clicking the add button', async () => {
// // See settings_test for specs that cover the actual prompt
// await this.app.client.click('#addServerButton').pause(500);
// const isModalExisting = await this.app.client.isExisting('#newServerModal');
// isModalExisting.should.be.true;
// });
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,99 +0,0 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
'use strict';
import assert from 'assert';
import 'airbnb-js-shims/target/es2015';
import TrustedOriginsStore from '../../../src/main/trustedOrigins.js';
import {BASIC_AUTH_PERMISSION} from '../../../src/common/permissions.js';
function mockTOS(fileName, returnvalue) {
const tos = new TrustedOriginsStore(fileName);
tos.readFromFile = () => {
return returnvalue;
};
return tos;
}
describe('Trusted Origins', () => {
describe('validate load', () => {
it('should be empty if there is no file', () => {
const tos = mockTOS('emptyfile', null);
tos.load();
assert.deepEqual(tos.data.size, 0);
});
it('should throw an error if data isn\'t an object', () => {
const tos = mockTOS('notobject', 'this is not my object!');
assert.throws(tos.load, SyntaxError);
});
it('should throw an error if data isn\'t in the expected format', () => {
const tos = mockTOS('badobject', '{"https://mattermost.com": "this is not my object!"}');
assert.throws(tos.load, /^Error: Provided TrustedOrigins file does not validate, using defaults instead\.$/);
});
it('should drop keys that aren\'t urls', () => {
const tos = mockTOS('badobject2', `{"this is not an uri": {"${BASIC_AUTH_PERMISSION}": true}}`);
tos.load();
assert.equal(typeof tos.data['this is not an uri'], 'undefined');
});
it('should contain valid data if everything goes right', () => {
const value = {
'https://mattermost.com': {
[BASIC_AUTH_PERMISSION]: true,
}};
const tos = mockTOS('okfile', JSON.stringify(value));
tos.load();
assert.deepEqual(Object.fromEntries(tos.data.entries()), value);
});
});
describe('validate testing permissions', () => {
const value = {
'https://mattermost.com': {
[BASIC_AUTH_PERMISSION]: true,
},
'https://notmattermost.com': {
[BASIC_AUTH_PERMISSION]: false,
},
};
const tos = mockTOS('permission_test', JSON.stringify(value));
tos.load();
it('tos should contain 2 elements', () => {
assert.equal(tos.data.size, 2);
});
it('should say ok if the permission is set', () => {
assert.equal(tos.checkPermission('https://mattermost.com', BASIC_AUTH_PERMISSION), true);
});
it('should say ko if the permission is set to false', () => {
assert.equal(tos.checkPermission('https://notmattermost.com', BASIC_AUTH_PERMISSION), false);
});
it('should say ko if the uri is not set', () => {
assert.equal(tos.checkPermission('https://undefined.com', BASIC_AUTH_PERMISSION), null);
});
it('should say null if the permission is unknown', () => {
assert.equal(tos.checkPermission('https://mattermost.com'), null);
});
});
describe('validate deleting permissions', () => {
const value = {
'https://mattermost.com': {
[BASIC_AUTH_PERMISSION]: true,
},
'https://notmattermost.com': {
[BASIC_AUTH_PERMISSION]: false,
},
};
const tos = mockTOS('permission_test', JSON.stringify(value));
tos.load();
it('deleting revokes access', () => {
assert.equal(tos.checkPermission('https://mattermost.com', BASIC_AUTH_PERMISSION), true);
tos.delete('https://mattermost.com');
assert.equal(tos.checkPermission('https://mattermost.com', BASIC_AUTH_PERMISSION), null);
});
});
});

View File

@@ -5,65 +5,65 @@ import assert from 'assert';
import UserActivityMonitor from '../../../src/main/UserActivityMonitor';
describe('UserActivityMonitor', () => {
describe('updateIdleTime', () => {
it('should set idle time to provided value', () => {
const userActivityMonitor = new UserActivityMonitor();
const idleTime = Math.round(Date.now() / 1000);
userActivityMonitor.updateIdleTime(idleTime);
assert.equal(userActivityMonitor.userIdleTime, idleTime);
});
});
describe('updateUserActivityStatus', () => {
let userActivityMonitor;
beforeEach(() => {
userActivityMonitor = new UserActivityMonitor();
describe('updateIdleTime', () => {
it('should set idle time to provided value', () => {
const userActivityMonitor = new UserActivityMonitor();
const idleTime = Math.round(Date.now() / 1000);
userActivityMonitor.updateIdleTime(idleTime);
assert.equal(userActivityMonitor.userIdleTime, idleTime);
});
});
it('should set user status to active', () => {
userActivityMonitor.setActivityState(true);
assert.equal(userActivityMonitor.userIsActive, true);
});
it('should set user status to inactive', () => {
userActivityMonitor.setActivityState(false);
assert.equal(userActivityMonitor.userIsActive, false);
});
});
describe('updateUserActivityStatus', () => {
let userActivityMonitor;
describe('sendStatusUpdate', () => {
let userActivityMonitor;
beforeEach(() => {
userActivityMonitor = new UserActivityMonitor();
});
beforeEach(() => {
userActivityMonitor = new UserActivityMonitor();
it('should set user status to active', () => {
userActivityMonitor.setActivityState(true);
assert.equal(userActivityMonitor.userIsActive, true);
});
it('should set user status to inactive', () => {
userActivityMonitor.setActivityState(false);
assert.equal(userActivityMonitor.userIsActive, false);
});
});
it('should emit a non-system triggered status event indicating a user is active', () => {
userActivityMonitor.on('status', ({userIsActive, isSystemEvent}) => {
assert.equal(userIsActive && !isSystemEvent, true);
});
userActivityMonitor.setActivityState(true, false);
});
describe('sendStatusUpdate', () => {
let userActivityMonitor;
it('should emit a non-system triggered status event indicating a user is inactive', () => {
userActivityMonitor.on('status', ({userIsActive, isSystemEvent}) => {
assert.equal(!userIsActive && !isSystemEvent, true);
});
userActivityMonitor.setActivityState(false, false);
});
beforeEach(() => {
userActivityMonitor = new UserActivityMonitor();
});
it('should emit a system triggered status event indicating a user is active', () => {
userActivityMonitor.on('status', ({userIsActive, isSystemEvent}) => {
assert.equal(userIsActive && isSystemEvent, true);
});
userActivityMonitor.setActivityState(true, true);
});
it('should emit a non-system triggered status event indicating a user is active', () => {
userActivityMonitor.on('status', ({userIsActive, isSystemEvent}) => {
assert.equal(userIsActive && !isSystemEvent, true);
});
userActivityMonitor.setActivityState(true, false);
});
it('should emit a system triggered status event indicating a user is inactive', () => {
userActivityMonitor.on('status', ({userIsActive, isSystemEvent}) => {
assert.equal(!userIsActive && isSystemEvent, true);
});
userActivityMonitor.setActivityState(false, true);
it('should emit a non-system triggered status event indicating a user is inactive', () => {
userActivityMonitor.on('status', ({userIsActive, isSystemEvent}) => {
assert.equal(!userIsActive && !isSystemEvent, true);
});
userActivityMonitor.setActivityState(false, false);
});
it('should emit a system triggered status event indicating a user is active', () => {
userActivityMonitor.on('status', ({userIsActive, isSystemEvent}) => {
assert.equal(userIsActive && isSystemEvent, true);
});
userActivityMonitor.setActivityState(true, true);
});
it('should emit a system triggered status event indicating a user is inactive', () => {
userActivityMonitor.on('status', ({userIsActive, isSystemEvent}) => {
assert.equal(!userIsActive && isSystemEvent, true);
});
userActivityMonitor.setActivityState(false, true);
});
});
});
});

View File

@@ -3,123 +3,124 @@
// See LICENSE.txt for license information.
'use strict';
const path = require('path');
const fs = require('fs');
const http = require('http');
// const fs = require('fs');
const env = require('../modules/environment');
// const path = require('path');
// const http = require('http');
describe.skip('security', function desc() {
this.timeout(30000);
// const env = require('../modules/environment');
const serverPort = 8181;
const testURL = `http://localhost:${serverPort}`;
// describe.skip('security', function desc() {
// this.timeout(30000);
const config = {
version: 2,
teams: [{
name: 'example_1',
url: testURL,
order: 0,
}, {
name: 'example_2',
url: testURL,
order: 1,
}],
};
// const serverPort = 8181;
// const testURL = `http://localhost:${serverPort}`;
before(() => {
this.server = http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/html',
});
res.end(fs.readFileSync(path.resolve(env.sourceRootDir, 'test/modules/test.html'), 'utf-8'));
}).listen(serverPort, '127.0.0.1');
});
// const config = {
// version: 2,
// teams: [{
// name: 'example_1',
// url: testURL,
// order: 0,
// }, {
// name: 'example_2',
// url: testURL,
// order: 1,
// }],
// };
beforeEach(() => {
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
this.app = env.getSpectronApp();
return this.app.start();
});
// before(() => {
// this.server = http.createServer((req, res) => {
// res.writeHead(200, {
// 'Content-Type': 'text/html',
// });
// res.end(fs.readFileSync(path.resolve(env.sourceRootDir, 'test/modules/test.html'), 'utf-8'));
// }).listen(serverPort, '127.0.0.1');
// });
afterEach(() => {
if (this.app && this.app.isRunning()) {
return this.app.stop();
}
return true;
});
// beforeEach(() => {
// fs.writeFileSync(env.configFilePath, JSON.stringify(config));
// this.app = env.getSpectronApp();
// return this.app.start();
// });
after((done) => {
this.server.close(done);
});
// afterEach(() => {
// if (this.app && this.app.isRunning()) {
// return this.app.stop();
// }
// return true;
// });
it('should NOT be able to call Node.js API in webview', () => {
env.addClientCommands(this.app.client);
// after((done) => {
// this.server.close(done);
// });
// webview is handled as a window by chromedriver.
return this.app.client.
windowByIndex(1).isNodeEnabled().then((enabled) => {
enabled.should.be.false;
}).
windowByIndex(2).isNodeEnabled().then((enabled) => {
enabled.should.be.false;
}).
windowByIndex(0).
getAttribute('webview', 'nodeintegration').then((nodeintegration) => {
// nodeintegration is an array of string
nodeintegration.forEach((n) => {
n.should.equal('false');
});
});
});
// it('should NOT be able to call Node.js API in webview', () => {
// env.addClientCommands(this.app.client);
it('should NOT be able to call Node.js API in a new window', () => {
env.addClientCommands(this.app.client);
const client = this.app.client;
return this.app.client.
windowByIndex(1). // in the first webview
execute(() => {
open_window();
}).
waitUntil(() => {
return client.windowHandles().then((handles) => {
return handles.value.length === 4;
});
}, 5000, 'expected a new window').
windowByIndex(3).isNodeEnabled().then((enabled) => {
enabled.should.be.false;
});
});
// // webview is handled as a window by chromedriver.
// return this.app.client.
// windowByIndex(1).isNodeEnabled().then((enabled) => {
// enabled.should.be.false;
// }).
// windowByIndex(2).isNodeEnabled().then((enabled) => {
// enabled.should.be.false;
// }).
// windowByIndex(0).
// getAttribute('webview', 'nodeintegration').then((nodeintegration) => {
// // nodeintegration is an array of string
// nodeintegration.forEach((n) => {
// n.should.equal('false');
// });
// });
// });
it('should NOT be able to call eval() in any window', () => {
env.addClientCommands(this.app.client);
const tryEval = (index) => {
return this.app.client.
windowByIndex(index).
execute(() => {
return eval('1 + 1');
}).then((result) => {
throw new Error(`Promise was unexpectedly fulfilled (result: ${result})`);
}, (error) => {
(error !== null).should.be.true;
});
};
const tryEvalInSettingsPage = () => {
return this.app.client.
windowByIndex(0).
loadSettingsPage().
execute(() => {
return eval('1 + 1');
}).then((result) => {
throw new Error(`Promise was unexpectedly fulfilled (result: ${result})`);
}, (error) => {
(error !== null).should.be.true;
});
};
return Promise.all([
tryEval(0),
tryEvalInSettingsPage(),
]);
});
});
// it('should NOT be able to call Node.js API in a new window', () => {
// env.addClientCommands(this.app.client);
// const client = this.app.client;
// return this.app.client.
// windowByIndex(1). // in the first webview
// execute(() => {
// open_window();
// }).
// waitUntil(() => {
// return client.windowHandles().then((handles) => {
// return handles.value.length === 4;
// });
// }, 5000, 'expected a new window').
// windowByIndex(3).isNodeEnabled().then((enabled) => {
// enabled.should.be.false;
// });
// });
// it('should NOT be able to call eval() in any window', () => {
// env.addClientCommands(this.app.client);
// const tryEval = (index) => {
// return this.app.client.
// windowByIndex(index).
// execute(() => {
// return eval('1 + 1');
// }).then((result) => {
// throw new Error(`Promise was unexpectedly fulfilled (result: ${result})`);
// }, (error) => {
// (error !== null).should.be.true;
// });
// };
// const tryEvalInSettingsPage = () => {
// return this.app.client.
// windowByIndex(0).
// loadSettingsPage().
// execute(() => {
// return eval('1 + 1');
// }).then((result) => {
// throw new Error(`Promise was unexpectedly fulfilled (result: ${result})`);
// }, (error) => {
// (error !== null).should.be.true;
// });
// };
// return Promise.all([
// tryEval(0),
// tryEvalInSettingsPage(),
// ]);
// });
// });

View File

@@ -1,219 +1,152 @@
// Copyright (c) 2015-2016 Yuya Ochiai
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import path from 'path';
//import path from 'path';
import SpellChecker from '../../src/main/SpellChecker';
// TODO: reenable with the new spellchecker
describe('main/Spellchecker.js', function() {
describe('getSpellCheckerLocale()', () => {
it('should return recognized locale', () => {
SpellChecker.getSpellCheckerLocale('en').should.equal('en-US');
SpellChecker.getSpellCheckerLocale('en-US').should.equal('en-US');
// describe('main/Spellchecker.js', function() {
// describe('getSpellCheckerLocale()', () => {
// it('should return recognized locale', () => {
// // SpellChecker.getSpellCheckerLocale('en').should.equal('en-US');
// // SpellChecker.getSpellCheckerLocale('en-US').should.equal('en-US');
SpellChecker.getSpellCheckerLocale('fr').should.equal('fr-FR');
SpellChecker.getSpellCheckerLocale('fr-FR').should.equal('fr-FR');
// // SpellChecker.getSpellCheckerLocale('fr').should.equal('fr-FR');
// // SpellChecker.getSpellCheckerLocale('fr-FR').should.equal('fr-FR');
SpellChecker.getSpellCheckerLocale('de').should.equal('de-DE');
SpellChecker.getSpellCheckerLocale('de-DE').should.equal('de-DE');
// // SpellChecker.getSpellCheckerLocale('de').should.equal('de-DE');
// // SpellChecker.getSpellCheckerLocale('de-DE').should.equal('de-DE');
SpellChecker.getSpellCheckerLocale('es').should.equal('es-ES');
SpellChecker.getSpellCheckerLocale('es-ES').should.equal('es-ES');
// // SpellChecker.getSpellCheckerLocale('es').should.equal('es-ES');
// // SpellChecker.getSpellCheckerLocale('es-ES').should.equal('es-ES');
SpellChecker.getSpellCheckerLocale('nl').should.equal('nl-NL');
SpellChecker.getSpellCheckerLocale('nl-NL').should.equal('nl-NL');
// // SpellChecker.getSpellCheckerLocale('nl').should.equal('nl-NL');
// // SpellChecker.getSpellCheckerLocale('nl-NL').should.equal('nl-NL');
SpellChecker.getSpellCheckerLocale('pl').should.equal('pl-PL');
SpellChecker.getSpellCheckerLocale('pl-PL').should.equal('pl-PL');
SpellChecker.getSpellCheckerLocale('pt').should.equal('pt-BR');
SpellChecker.getSpellCheckerLocale('pt-BR').should.equal('pt-BR');
// // SpellChecker.getSpellCheckerLocale('pl').should.equal('pl-PL');
// // SpellChecker.getSpellCheckerLocale('pl-PL').should.equal('pl-PL');
// // SpellChecker.getSpellCheckerLocale('pt').should.equal('pt-BR');
// // SpellChecker.getSpellCheckerLocale('pt-BR').should.equal('pt-BR');
SpellChecker.getSpellCheckerLocale('ja').should.equal('en-US');
SpellChecker.getSpellCheckerLocale('ja-JP').should.equal('en-US');
// // SpellChecker.getSpellCheckerLocale('ja').should.equal('en-US');
// // SpellChecker.getSpellCheckerLocale('ja-JP').should.equal('en-US');
SpellChecker.getSpellCheckerLocale('it').should.equal('it-IT');
SpellChecker.getSpellCheckerLocale('it-IT').should.equal('it-IT');
// // SpellChecker.getSpellCheckerLocale('it').should.equal('it-IT');
// // SpellChecker.getSpellCheckerLocale('it-IT').should.equal('it-IT');
// });
// });
SpellChecker.getSpellCheckerLocale('ru').should.equal('ru-RU');
SpellChecker.getSpellCheckerLocale('ru-RU').should.equal('ru-RU');
// describe('en-US', function() {
// const spellchecker = null;
SpellChecker.getSpellCheckerLocale('sv').should.equal('sv-SE');
SpellChecker.getSpellCheckerLocale('sv-SE').should.equal('sv-SE');
// // before(function(done) {
// // // spellchecker = new SpellChecker(
// // // 'en-US',
// // // path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
// // // done
// // // );
// // });
SpellChecker.getSpellCheckerLocale('uk').should.equal('uk-UA');
SpellChecker.getSpellCheckerLocale('uk-UA').should.equal('uk-UA');
});
});
// it('should spellcheck', function() {
// // https://github.com/jfmdev/simple-spellchecker/issues/3
// spellchecker.spellCheck('spell').should.equal(true);
// spellchecker.spellCheck('spel').should.equal(false);
// spellchecker.spellCheck('December').should.equal(true);
// spellchecker.spellCheck('december').should.equal(true);
// spellchecker.spellCheck('English').should.equal(true);
// spellchecker.spellCheck('Japan').should.equal(true);
// });
describe('en-US', function() {
let spellchecker = null;
// it('should allow contractions', function() {
// spellchecker.spellCheck("shouldn't").should.equal(true);
// spellchecker.spellCheck('shouldn').should.equal(true);
// });
before(function(done) {
spellchecker = new SpellChecker(
'en-US',
path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
done
);
});
// it('should allow numerals', function() {
// spellchecker.spellCheck('1').should.equal(true);
// spellchecker.spellCheck('-100').should.equal(true);
// spellchecker.spellCheck('3.14').should.equal(true);
// });
it('should spellcheck', function() {
// https://github.com/jfmdev/simple-spellchecker/issues/3
spellchecker.spellCheck('spell').should.equal(true);
spellchecker.spellCheck('spel').should.equal(false);
spellchecker.spellCheck('December').should.equal(true);
spellchecker.spellCheck('december').should.equal(true);
spellchecker.spellCheck('English').should.equal(true);
spellchecker.spellCheck('Japan').should.equal(true);
});
// it('should allow "Mattermost"', function() {
// spellchecker.spellCheck('Mattermost').should.equal(true);
// spellchecker.spellCheck('mattermost').should.equal(true);
// });
it('should allow contractions', function() {
spellchecker.spellCheck("shouldn't").should.equal(true);
spellchecker.spellCheck('shouldn').should.equal(true);
});
// it('should give at most the requested number of suggestions', function() {
// // helllo known to give at least 4 suggestions
// spellchecker.getSuggestions('helllo', 4).length.should.be.equal(4);
// spellchecker.getSuggestions('helllo', 1).length.should.be.equal(1);
// });
it('should allow numerals', function() {
spellchecker.spellCheck('1').should.equal(true);
spellchecker.spellCheck('-100').should.equal(true);
spellchecker.spellCheck('3.14').should.equal(true);
});
// it('should give suggestions which preserve case of first letter', function() {
// let suggestions = spellchecker.getSuggestions('carr', 4);
// suggestions.length.should.not.be.equal(0);
// let i;
// for (i = 0; i < suggestions.length; i++) {
// suggestions[i].charAt(0).should.be.equal('c');
// }
it('should allow "Mattermost"', function() {
spellchecker.spellCheck('Mattermost').should.equal(true);
spellchecker.spellCheck('mattermost').should.equal(true);
});
// suggestions = spellchecker.getSuggestions('Carr', 4);
// suggestions.length.should.not.be.equal(0);
// for (i = 0; i < suggestions.length; i++) {
// suggestions[i].charAt(0).should.be.equal('C');
// }
// });
// });
it('should give at most the requested number of suggestions', function() {
// helllo known to give at least 4 suggestions
spellchecker.getSuggestions('helllo', 4).length.should.be.equal(4);
spellchecker.getSuggestions('helllo', 1).length.should.be.equal(1);
});
// describe('en-GB', function() {
// const spellchecker = null;
it('should give suggestions which preserve case of first letter', function() {
let suggestions = spellchecker.getSuggestions('carr', 4);
suggestions.length.should.not.be.equal(0);
let i;
for (i = 0; i < suggestions.length; i++) {
suggestions[i].charAt(0).should.be.equal('c');
}
// // before(function(done) {
// // spellchecker = new SpellChecker(
// // 'en-GB',
// // path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
// // done
// // );
// // });
suggestions = spellchecker.getSuggestions('Carr', 4);
suggestions.length.should.not.be.equal(0);
for (i = 0; i < suggestions.length; i++) {
suggestions[i].charAt(0).should.be.equal('C');
}
});
});
// it('should allow contractions', function() {
// spellchecker.spellCheck("shouldn't").should.equal(true);
// spellchecker.spellCheck('shouldn').should.equal(true);
// });
// });
describe('en-GB', function() {
let spellchecker = null;
// describe('de-DE', function() {
// const spellchecker = null;
before(function(done) {
spellchecker = new SpellChecker(
'en-GB',
path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
done
);
});
// // before(function(done) {
// // spellchecker = new SpellChecker(
// // 'de-DE',
// // path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
// // done
// // );
// // });
it('should allow contractions', function() {
spellchecker.spellCheck("shouldn't").should.equal(true);
spellchecker.spellCheck('shouldn').should.equal(true);
});
});
// it('should spellcheck', function() {
// spellchecker.spellCheck('Guten').should.equal(true);
// spellchecker.spellCheck('tag').should.equal(true);
// });
describe('ru-RU', function() {
let spellchecker = null;
// it('should allow numerals', function() {
// spellchecker.spellCheck('1').should.equal(true);
// spellchecker.spellCheck('-100').should.equal(true);
// spellchecker.spellCheck('3.14').should.equal(true);
// });
before(function(done) {
spellchecker = new SpellChecker(
'ru-RU',
path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
done
);
});
// it('should give suggestions which preserve case of first letter', function() {
// let suggestions = spellchecker.getSuggestions('gutenn', 4);
// suggestions.length.should.not.be.equal(0);
// let i;
// for (i = 0; i < suggestions.length; i++) {
// suggestions[i].charAt(0).should.be.equal('g');
// }
it('should spellcheck', function() {
spellchecker.spellCheck('русский').should.equal(true);
});
it('should give suggestions', function() {
spellchecker.getSuggestions('руский', 1).length.should.be.equal(1);
});
});
describe('sv-SE', function() {
let spellchecker = null;
before(function(done) {
spellchecker = new SpellChecker(
'sv-SE',
path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
done
);
});
it('should spellcheck', function() {
spellchecker.spellCheck('ändamålslös').should.equal(true);
spellchecker.spellCheck('ändamålslos').should.equal(false);
});
it('should give suggestions', function() {
spellchecker.getSuggestions('ändamålslos', 1).length.should.be.equal(1);
});
});
describe('uk-UA', function() {
let spellchecker = null;
before(function(done) {
spellchecker = new SpellChecker(
'uk-UA',
path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
done
);
});
it('should spellcheck', function() {
spellchecker.spellCheck('українська').should.equal(true);
});
it('should give suggestions', function() {
spellchecker.getSuggestions('украінська', 1).length.should.be.equal(1);
});
});
describe('de-DE', function() {
let spellchecker = null;
before(function(done) {
spellchecker = new SpellChecker(
'de-DE',
path.resolve(__dirname, '../../src/node_modules/simple-spellchecker/dict'),
done
);
});
it('should spellcheck', function() {
spellchecker.spellCheck('Guten').should.equal(true);
spellchecker.spellCheck('tag').should.equal(true);
});
it('should allow numerals', function() {
spellchecker.spellCheck('1').should.equal(true);
spellchecker.spellCheck('-100').should.equal(true);
spellchecker.spellCheck('3.14').should.equal(true);
});
it('should give suggestions which preserve case of first letter', function() {
let suggestions = spellchecker.getSuggestions('gutenn', 4);
suggestions.length.should.not.be.equal(0);
let i;
for (i = 0; i < suggestions.length; i++) {
suggestions[i].charAt(0).should.be.equal('g');
}
suggestions = spellchecker.getSuggestions('Gutenn', 4);
suggestions.length.should.not.be.equal(0);
for (i = 0; i < suggestions.length; i++) {
suggestions[i].charAt(0).should.be.equal('G');
}
});
});
});
// suggestions = spellchecker.getSuggestions('Gutenn', 4);
// suggestions.length.should.not.be.equal(0);
// for (i = 0; i < suggestions.length; i++) {
// suggestions[i].charAt(0).should.be.equal('G');
// }
// });
// });
// });

View File

@@ -1,106 +0,0 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
'use strict';
import assert from 'assert';
import urlUtils from '../../../src/utils/url';
describe('url', () => {
describe('isValidURL', () => {
it('should be true for a valid web url', () => {
const testURL = 'https://developers.mattermost.com/';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a valid, non-https web url', () => {
const testURL = 'http://developers.mattermost.com/';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for an invalid, self-defined, top-level domain', () => {
const testURL = 'https://www.example.x';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a file download url', () => {
const testURL = 'https://community.mattermost.com/api/v4/files/ka3xbfmb3ffnmgdmww8otkidfw?download=1';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a permalink url', () => {
const testURL = 'https://community.mattermost.com/test-channel/pl/pdqowkij47rmbyk78m5hwc7r6r';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a valid, internal domain', () => {
const testURL = 'https://mattermost.company-internal';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a second, valid internal domain', () => {
const testURL = 'https://serverXY/mattermost';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a valid, non-https internal domain', () => {
const testURL = 'http://mattermost.local';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a valid, non-https, ip address with port number', () => {
const testURL = 'http://localhost:8065';
assert.equal(urlUtils.isValidURL(testURL), true);
});
});
describe('isValidURI', () => {
it('should be true for a deeplink url', () => {
const testURL = 'mattermost://community-release.mattermost.com/core/channels/developers';
assert.equal(urlUtils.isValidURI(testURL), true);
});
it('should be false for a malicious url', () => {
const testURL = String.raw`mattermost:///" --data-dir "\\deans-mbp\mattermost`;
assert.equal(urlUtils.isValidURI(testURL), false);
});
});
describe('isInternalURL', () => {
it('should be false for different hosts', () => {
const currentURL = new URL('http://localhost/team/channel1');
const targetURL = new URL('http://example.com/team/channel2');
const basename = '/';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), false);
});
it('should be false for same hosts, non-matching basename', () => {
const currentURL = new URL('http://localhost/subpath/team/channel1');
const targetURL = new URL('http://localhost/team/channel2');
const basename = '/subpath';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), false);
});
it('should be true for same hosts, matching basename', () => {
const currentURL = new URL('http://localhost/subpath/team/channel1');
const targetURL = new URL('http://localhost/subpath/team/channel2');
const basename = '/subpath';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), true);
});
it('should be true for same hosts, default basename', () => {
const currentURL = new URL('http://localhost/team/channel1');
const targetURL = new URL('http://localhost/team/channel2');
const basename = '/';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), true);
});
it('should be true for same hosts, default basename, empty target path', () => {
const currentURL = new URL('http://localhost/team/channel1');
const targetURL = new URL('http://localhost/');
const basename = '/';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), true);
});
});
describe('getHost', () => {
it('should return the origin of a well formed url', () => {
const myurl = 'https://mattermost.com/download';
assert.equal(urlUtils.getHost(myurl), 'https://mattermost.com');
});
it('shoud raise an error on malformed urls', () => {
const myurl = 'http://example.com:-80/';
assert.throws(() => urlUtils.getHost(myurl), Error);
});
});
});

View File

@@ -0,0 +1,106 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
'use strict';
import assert from 'assert';
import urlUtils from '../../../src/common/utils/url';
describe('Utils', () => {
describe('isValidURL', () => {
it('should be true for a valid web url', () => {
const testURL = 'https://developers.mattermost.com/';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a valid, non-https web url', () => {
const testURL = 'http://developers.mattermost.com/';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for an invalid, self-defined, top-level domain', () => {
const testURL = 'https://www.example.x';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a file download url', () => {
const testURL = 'https://community.mattermost.com/api/v4/files/ka3xbfmb3ffnmgdmww8otkidfw?download=1';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a permalink url', () => {
const testURL = 'https://community.mattermost.com/test-channel/pl/pdqowkij47rmbyk78m5hwc7r6r';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a valid, internal domain', () => {
const testURL = 'https://mattermost.company-internal';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a second, valid internal domain', () => {
const testURL = 'https://serverXY/mattermost';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a valid, non-https internal domain', () => {
const testURL = 'http://mattermost.local';
assert.equal(urlUtils.isValidURL(testURL), true);
});
it('should be true for a valid, non-https, ip address with port number', () => {
const testURL = 'http://localhost:8065';
assert.equal(urlUtils.isValidURL(testURL), true);
});
});
describe('isValidURI', () => {
it('should be true for a deeplink url', () => {
const testURL = 'mattermost://community-release.mattermost.com/core/channels/developers';
assert.equal(urlUtils.isValidURI(testURL), true);
});
it('should be false for a malicious url', () => {
const testURL = String.raw`mattermost:///" --data-dir "\\deans-mbp\mattermost`;
assert.equal(urlUtils.isValidURI(testURL), false);
});
});
describe('isInternalURL', () => {
it('should be false for different hosts', () => {
const currentURL = new URL('http://localhost/team/channel1');
const targetURL = new URL('http://example.com/team/channel2');
const basename = '/';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), false);
});
it('should be false for same hosts, non-matching basename', () => {
const currentURL = new URL('http://localhost/subpath/team/channel1');
const targetURL = new URL('http://localhost/team/channel2');
const basename = '/subpath';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), false);
});
it('should be true for same hosts, matching basename', () => {
const currentURL = new URL('http://localhost/subpath/team/channel1');
const targetURL = new URL('http://localhost/subpath/team/channel2');
const basename = '/subpath';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), true);
});
it('should be true for same hosts, default basename', () => {
const currentURL = new URL('http://localhost/team/channel1');
const targetURL = new URL('http://localhost/team/channel2');
const basename = '/';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), true);
});
it('should be true for same hosts, default basename, empty target path', () => {
const currentURL = new URL('http://localhost/team/channel1');
const targetURL = new URL('http://localhost/');
const basename = '/';
assert.equal(urlUtils.isInternalURL(targetURL, currentURL, basename), true);
});
});
describe('getHost', () => {
it('should return the origin of a well formed url', () => {
const myurl = 'https://mattermost.com/download';
assert.equal(urlUtils.getHost(myurl), 'https://mattermost.com');
});
it('shoud raise an error on malformed urls', () => {
const myurl = 'http://example.com:-80/';
assert.throws(() => urlUtils.getHost(myurl), Error);
});
});
});

4
test/unit/index.js Normal file
View File

@@ -0,0 +1,4 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import './trusted_origins_test';

View File

@@ -0,0 +1,99 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
'use strict';
import assert from 'assert';
import 'airbnb-js-shims/target/es2015';
import TrustedOriginsStore from 'main/trustedOrigins.js';
import {BASIC_AUTH_PERMISSION} from 'common/permissions.js';
function mockTOS(fileName, returnvalue) {
const tos = new TrustedOriginsStore(fileName);
tos.readFromFile = () => {
return returnvalue;
};
return tos;
}
describe('Trusted Origins', () => {
describe('validate load', () => {
it('should be empty if there is no file', () => {
const tos = mockTOS('emptyfile', null);
tos.load();
assert.deepEqual(tos.data.size, 0);
});
it('should throw an error if data isn\'t an object', () => {
const tos = mockTOS('notobject', 'this is not my object!');
assert.throws(tos.load, SyntaxError);
});
it('should throw an error if data isn\'t in the expected format', () => {
const tos = mockTOS('badobject', '{"https://mattermost.com": "this is not my object!"}');
assert.throws(tos.load, /^Error: Provided TrustedOrigins file does not validate, using defaults instead\.$/);
});
it('should drop keys that aren\'t urls', () => {
const tos = mockTOS('badobject2', `{"this is not an uri": {"${BASIC_AUTH_PERMISSION}": true}}`);
tos.load();
assert.equal(typeof tos.data['this is not an uri'], 'undefined');
});
it('should contain valid data if everything goes right', () => {
const value = {
'https://mattermost.com': {
[BASIC_AUTH_PERMISSION]: true,
}};
const tos = mockTOS('okfile', JSON.stringify(value));
tos.load();
assert.deepEqual(Object.fromEntries(tos.data.entries()), value);
});
});
describe('validate testing permissions', () => {
const value = {
'https://mattermost.com': {
[BASIC_AUTH_PERMISSION]: true,
},
'https://notmattermost.com': {
[BASIC_AUTH_PERMISSION]: false,
},
};
const tos = mockTOS('permission_test', JSON.stringify(value));
tos.load();
it('tos should contain 2 elements', () => {
assert.equal(tos.data.size, 2);
});
it('should say ok if the permission is set', () => {
assert.equal(tos.checkPermission('https://mattermost.com', BASIC_AUTH_PERMISSION), true);
});
it('should say ko if the permission is set to false', () => {
assert.equal(tos.checkPermission('https://notmattermost.com', BASIC_AUTH_PERMISSION), false);
});
it('should say ko if the uri is not set', () => {
assert.equal(tos.checkPermission('https://undefined.com', BASIC_AUTH_PERMISSION), null);
});
it('should say null if the permission is unknown', () => {
assert.equal(tos.checkPermission('https://mattermost.com'), null);
});
});
describe('validate deleting permissions', () => {
const value = {
'https://mattermost.com': {
[BASIC_AUTH_PERMISSION]: true,
},
'https://notmattermost.com': {
[BASIC_AUTH_PERMISSION]: false,
},
};
const tos = mockTOS('permission_test', JSON.stringify(value));
tos.load();
it('deleting revokes access', () => {
assert.equal(tos.checkPermission('https://mattermost.com', BASIC_AUTH_PERMISSION), true);
tos.delete('https://mattermost.com');
assert.equal(tos.checkPermission('https://mattermost.com', BASIC_AUTH_PERMISSION), null);
});
});
});