From ac4ac62c5933542243a990001f07e50fcd51d86a Mon Sep 17 00:00:00 2001 From: Oskar Date: Fri, 20 Sep 2024 16:29:26 +0200 Subject: [PATCH] Fix linting errors --- gui/scripts/extract-translations.js | 2 +- gui/scripts/verify-translations-format.ts | 20 ++-- gui/src/main/daemon-rpc.ts | 2 - gui/src/main/index.ts | 5 +- gui/src/main/linux-desktop-entry.ts | 1 - gui/src/main/linux-split-tunneling.ts | 4 +- gui/src/main/logging.ts | 2 +- gui/src/main/user-interface.ts | 2 +- gui/src/main/windows-split-tunneling.ts | 2 +- gui/src/renderer/app.tsx | 2 +- .../renderer/components/ClipboardLabel.tsx | 2 +- .../components/EditApiAccessMethod.tsx | 2 +- gui/src/renderer/components/ImageView.tsx | 1 - gui/src/renderer/components/Login.tsx | 2 +- .../components/NotificationBanner.tsx | 2 +- gui/src/renderer/components/ProblemReport.tsx | 6 +- .../main-view/FeatureIndicators.tsx | 2 +- gui/src/renderer/lib/ip.ts | 18 ++-- gui/tasks/assets.js | 9 +- gui/tasks/distribution.js | 96 +++++++++++++------ gui/tasks/scripts.js | 4 +- gui/test/e2e/installed/installed-utils.ts | 2 +- gui/test/e2e/installed/playwright.config.ts | 2 +- .../api-access-methods.spec.ts | 30 +++--- .../state-dependent/custom-bridge.spec.ts | 73 ++++++-------- .../state-dependent/device-revoked.spec.ts | 8 +- .../state-dependent/disconnected.spec.ts | 2 +- .../state-dependent/location.spec.ts | 3 +- .../installed/state-dependent/login.spec.ts | 76 ++++++++------- .../macos-split-tunneling.spec.ts | 29 +++--- .../state-dependent/obfuscation.spec.ts | 36 +++---- .../state-dependent/settings-import.spec.ts | 64 ++++++------- .../state-dependent/too-many-devices.spec.ts | 19 ++-- .../state-dependent/tunnel-state.spec.ts | 16 ++-- .../mocked/expired-account-error-view.spec.ts | 23 +++-- .../e2e/mocked/feature-indicators.spec.ts | 39 ++++---- gui/test/e2e/mocked/mocked-utils.ts | 4 +- gui/test/e2e/mocked/settings.spec.ts | 4 +- gui/test/e2e/mocked/tunnel-state.spec.ts | 15 ++- gui/test/e2e/shared/tunnel-state.ts | 1 + gui/test/e2e/utils.ts | 57 +++++------ gui/test/unit/account-data-cache.spec.ts | 15 ++- gui/test/unit/changelog.spec.ts | 3 +- gui/test/unit/date-helper.spec.ts | 88 +++++++---------- gui/test/unit/history.spec.ts | 3 +- gui/test/unit/ip.spec.ts | 3 +- gui/test/unit/keyframe-animation.spec.ts | 3 +- gui/test/unit/list-diff.spec.ts | 3 +- gui/test/unit/logging.spec.ts | 7 +- gui/test/unit/notification-evaluation.spec.ts | 36 ++++--- gui/test/unit/setup.ts | 2 +- gui/test/unit/tunnel-state.spec.ts | 4 +- 52 files changed, 453 insertions(+), 403 deletions(-) diff --git a/gui/scripts/extract-translations.js b/gui/scripts/extract-translations.js index 30e04e298aec..e101ca03a521 100644 --- a/gui/scripts/extract-translations.js +++ b/gui/scripts/extract-translations.js @@ -1,4 +1,4 @@ -const { GettextExtractor, JsExtractors, HtmlExtractors } = require('gettext-extractor'); +const { GettextExtractor, JsExtractors } = require('gettext-extractor'); const path = require('path'); const extractor = new GettextExtractor(); diff --git a/gui/scripts/verify-translations-format.ts b/gui/scripts/verify-translations-format.ts index e44997d579ed..eaba3ee7b71e 100644 --- a/gui/scripts/verify-translations-format.ts +++ b/gui/scripts/verify-translations-format.ts @@ -62,14 +62,14 @@ function checkFormatSpecifiers(translation: GetTextTranslation): boolean { .every((result) => result); } -function checkHtmlTagsImpl(value: string): { correct: boolean, amount: number } { - const tagsRegexp = new RegExp("<.*?>", "g"); +function checkHtmlTagsImpl(value: string): { correct: boolean; amount: number } { + const tagsRegexp = new RegExp('<.*?>', 'g'); const tags = value.match(tagsRegexp) ?? []; const tagTypes = tags.map((tag) => tag.slice(1, -1)); // Make sure tags match by pushing start-tags to a stack and matching closing tags with the last // item. - let tagStack: string[] = []; + const tagStack: string[] = []; for (let tag of tagTypes) { const selfClosing = tag.endsWith('/'); const endTag = tag.startsWith('/'); @@ -94,21 +94,23 @@ function checkHtmlTagsImpl(value: string): { correct: boolean, amount: number } } if (tagStack.length > 0) { - console.error(`Missing closing-tags (${tagStack}) in "${value}"`); - return { correct: false, amount: NaN }; + console.error(`Missing closing-tags (${tagStack}) in "${value}"`); + return { correct: false, amount: NaN }; } return { correct: true, amount: tags.length / 2 }; } function checkHtmlTags(translation: GetTextTranslation): boolean { - let { correct, amount: sourceAmount } = checkHtmlTagsImpl(translation.msgid); + const { correct, amount: sourceAmount } = checkHtmlTagsImpl(translation.msgid); - let translationsCorrect = translation.msgstr.every((value) => { - let { correct, amount } = checkHtmlTagsImpl(value); + const translationsCorrect = translation.msgstr.every((value) => { + const { correct, amount } = checkHtmlTagsImpl(value); // The amount doesn't make sense if the string isn't correctly formatted. if (correct && amount !== sourceAmount) { - console.error(`Incorrect amount of tags in translation for "${translation.msgid}": "${value}"`); + console.error( + `Incorrect amount of tags in translation for "${translation.msgid}": "${value}"`, + ); } return correct && amount === sourceAmount; }); diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 31f6ddcfabfb..1c96be12bbcd 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -791,7 +791,6 @@ export class DaemonRpc { } private channelOptions(): grpc.ClientOptions { - /* eslint-disable @typescript-eslint/naming-convention */ return { 'grpc.max_reconnect_backoff_ms': 3000, 'grpc.initial_reconnect_backoff_ms': 3000, @@ -799,7 +798,6 @@ export class DaemonRpc { 'grpc.keepalive_timeout_ms': Math.pow(2, 30), 'grpc.client_idle_timeout_ms': Math.pow(2, 30), }; - /* eslint-enable @typescript-eslint/naming-convention */ } private connectivityChangeCallback(timeoutErr?: Error) { diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts index c5144ee99e3c..be6c6609a03e 100644 --- a/gui/src/main/index.ts +++ b/gui/src/main/index.ts @@ -70,6 +70,7 @@ import Version, { GUI_VERSION } from './version'; const execAsync = util.promisify(exec); // Only import split tunneling library on correct OS. +// eslint-disable-next-line @typescript-eslint/no-require-imports const linuxSplitTunneling = process.platform === 'linux' && require('./linux-split-tunneling'); // This is used on Windows and macOS and will be undefined on Linux. const splitTunneling: ISplitTunnelingAppListRetriever | undefined = importSplitTunneling(); @@ -1115,11 +1116,11 @@ class ApplicationMain function importSplitTunneling() { if (process.platform === 'win32') { - // eslint-disable-next-line @typescript-eslint/no-var-requires + // eslint-disable-next-line @typescript-eslint/no-require-imports const { WindowsSplitTunnelingAppListRetriever } = require('./windows-split-tunneling'); return new WindowsSplitTunnelingAppListRetriever(); } else if (process.platform === 'darwin') { - // eslint-disable-next-line @typescript-eslint/no-var-requires + // eslint-disable-next-line @typescript-eslint/no-require-imports const { MacOsSplitTunnelingAppListRetriever } = require('./macos-split-tunneling'); return new MacOsSplitTunnelingAppListRetriever(); } diff --git a/gui/src/main/linux-desktop-entry.ts b/gui/src/main/linux-desktop-entry.ts index d6c11c79436d..7cc46862a5b0 100644 --- a/gui/src/main/linux-desktop-entry.ts +++ b/gui/src/main/linux-desktop-entry.ts @@ -265,7 +265,6 @@ function getGtkThemeDirectories(): Promise { process.env.ORIGINAL_XDG_CURRENT_DESKTOP ?? process.env.XDG_CURRENT_DESKTOP ?? ''; child_process.exec( 'gsettings get org.gnome.desktop.interface icon-theme', - // eslint-disable-next-line @typescript-eslint/naming-convention { env: { XDG_CURRENT_DESKTOP: xdgCurrentDesktop } }, (error, stdout) => { if (error) { diff --git a/gui/src/main/linux-split-tunneling.ts b/gui/src/main/linux-split-tunneling.ts index 3ae1a1539036..6d690aa453cf 100644 --- a/gui/src/main/linux-split-tunneling.ts +++ b/gui/src/main/linux-split-tunneling.ts @@ -113,7 +113,7 @@ export async function getApplications(locale: string): Promise - {obscured ? '●●●● ●●●● ●●●● ●●●●' : displayValue ?? value} + {obscured ? '●●●● ●●●● ●●●● ●●●●' : (displayValue ?? value)} {obscureValue !== false && ( { - const enabled = id === undefined ? true : method?.enabled ?? true; + const enabled = id === undefined ? true : (method?.enabled ?? true); updatedMethod.current = { ...newMethod, enabled }; if ( updatedMethod.current !== undefined && diff --git a/gui/src/renderer/components/ImageView.tsx b/gui/src/renderer/components/ImageView.tsx index fa3855b21a5a..f40a93fbbcfa 100644 --- a/gui/src/renderer/components/ImageView.tsx +++ b/gui/src/renderer/components/ImageView.tsx @@ -46,7 +46,6 @@ export default function ImageView(props: IImageViewProps) { ? props.source : `../../assets/images/${props.source}.svg`; - // eslint-disable-next-line @typescript-eslint/naming-convention const style = useMemo(() => ({ WebkitMaskImage: `url('${url}')` }), [url]); if (props.tintColor) { diff --git a/gui/src/renderer/components/Login.tsx b/gui/src/renderer/components/Login.tsx index 32eb5bbe82a3..e5f981eafb2b 100644 --- a/gui/src/renderer/components/Login.tsx +++ b/gui/src/renderer/components/Login.tsx @@ -281,7 +281,7 @@ export default class Login extends React.Component { await this.props.clearAccountHistory(); // TODO: Remove account from memory - } catch (error) { + } catch { // TODO: Show error } } diff --git a/gui/src/renderer/components/NotificationBanner.tsx b/gui/src/renderer/components/NotificationBanner.tsx index ad84ad369789..f79855f00458 100644 --- a/gui/src/renderer/components/NotificationBanner.tsx +++ b/gui/src/renderer/components/NotificationBanner.tsx @@ -167,7 +167,7 @@ export function NotificationBanner(props: INotificationBannerProps) { useEffect(() => { const newHeight = - props.children !== undefined ? contentRef.current?.getBoundingClientRect().height ?? 0 : 0; + props.children !== undefined ? (contentRef.current?.getBoundingClientRect().height ?? 0) : 0; if (newHeight !== contentHeight) { setContentHeight(newHeight); setAlignBottom((alignBottom) => alignBottom || contentHeight === 0 || newHeight === 0); diff --git a/gui/src/renderer/components/ProblemReport.tsx b/gui/src/renderer/components/ProblemReport.tsx index 3a0a9bbd0e9e..fd481cde07d7 100644 --- a/gui/src/renderer/components/ProblemReport.tsx +++ b/gui/src/renderer/components/ProblemReport.tsx @@ -137,7 +137,7 @@ function Form() { try { const reportId = await collectLog(); await viewLog(reportId); - } catch (error) { + } catch { // TODO: handle error } finally { setDisableActions(false); @@ -431,7 +431,7 @@ const ProblemReportContextProvider = ({ children }: { children: ReactNode }) => await sendProblemReport(email, message, reportId); clearReportForm(); setSendState(SendState.success); - } catch (error) { + } catch { setSendState(SendState.failed); } }, [email, message]); @@ -447,7 +447,7 @@ const ProblemReportContextProvider = ({ children }: { children: ReactNode }) => try { setSendState(SendState.sending); await sendReport(); - } catch (error) { + } catch { // No-op } } diff --git a/gui/src/renderer/components/main-view/FeatureIndicators.tsx b/gui/src/renderer/components/main-view/FeatureIndicators.tsx index 1696a6fd20ae..a4bf8659c65b 100644 --- a/gui/src/renderer/components/main-view/FeatureIndicators.tsx +++ b/gui/src/renderer/components/main-view/FeatureIndicators.tsx @@ -116,7 +116,7 @@ export default function FeatureIndicators(props: FeatureIndicatorsProps) { tunnelState.state === 'connected' || tunnelState.state === 'connecting'; const featureIndicators = useRef( - featureIndicatorsVisible ? tunnelState.featureIndicators ?? [] : [], + featureIndicatorsVisible ? (tunnelState.featureIndicators ?? []) : [], ); if (featureIndicatorsVisible && tunnelState.featureIndicators) { diff --git a/gui/src/renderer/lib/ip.ts b/gui/src/renderer/lib/ip.ts index 94eb807d491f..db458aa2560b 100644 --- a/gui/src/renderer/lib/ip.ts +++ b/gui/src/renderer/lib/ip.ts @@ -15,7 +15,7 @@ export abstract class IpAddress { public static fromString(ip: string): IPv4Address | IPv6Address { try { return IPv4Address.fromString(ip); - } catch (e) { + } catch { return IPv6Address.fromString(ip); } } @@ -80,7 +80,7 @@ export class IPv4Address extends IpAddress { try { const octets = IPv4Address.octetsFromString(ip); return new IPv4Address(octets); - } catch (e) { + } catch { throw new Error(`Invalid ip: ${ip}`); } } @@ -94,7 +94,7 @@ export class IPv4Address extends IpAddress { return parsedOctets; } } - } catch (e) { + } catch { // no-op } @@ -105,7 +105,7 @@ export class IPv4Address extends IpAddress { try { IPv4Address.fromString(ip); return true; - } catch (e) { + } catch { return false; } } @@ -135,7 +135,7 @@ export class IPv4Range extends IpRange { const prefixSize = parseInt(parts[1]); return new IPv4Range(octets, prefixSize); } - } catch (e) { + } catch { // no-op } @@ -167,7 +167,7 @@ export class IPv6Address extends IpAddress { try { const groups = IPv6Address.groupsFromString(ip); return new IPv6Address(groups); - } catch (e) { + } catch { throw new Error(`Invalid ip: ${ip}`); } } @@ -200,7 +200,7 @@ export class IPv6Address extends IpAddress { } } } - } catch (e) { + } catch { // no-op } @@ -211,7 +211,7 @@ export class IPv6Address extends IpAddress { try { IPv6Address.fromString(ip); return true; - } catch (e) { + } catch { return false; } } @@ -241,7 +241,7 @@ export class IPv6Range extends IpRange { const prefixSize = parseInt(parts[1], 10); return new IPv6Range(groups, prefixSize); } - } catch (e) { + } catch { // no-op } diff --git a/gui/tasks/assets.js b/gui/tasks/assets.js index 56b0c7a8ad56..802812418042 100644 --- a/gui/tasks/assets.js +++ b/gui/tasks/assets.js @@ -31,7 +31,14 @@ copyHtml.displayName = 'copy-html'; copyLocales.displayName = 'copy-locales'; copyGeoData.displayName = 'copy-geo-data'; -exports.copyAll = parallel(copyStaticAssets, copyConfig, copyCss, copyHtml, copyLocales, copyGeoData); +exports.copyAll = parallel( + copyStaticAssets, + copyConfig, + copyCss, + copyHtml, + copyLocales, + copyGeoData, +); exports.copyStaticAssets = copyStaticAssets; exports.copyCss = copyCss; exports.copyHtml = copyHtml; diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index a7f7c29d6d75..0e80c3c73f7c 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -42,7 +42,7 @@ const config = { name: 'mullvad-vpn', // We have to stick to semver on Windows for now due to: // https://github.com/electron-userland/electron-builder/issues/7173 - version: productVersion(process.platform === 'win32' ? ['semver'] : []) + version: productVersion(process.platform === 'win32' ? ['semver'] : []), }, files: [ @@ -139,23 +139,44 @@ const config = { publisherName: 'Mullvad VPN AB', extraResources: [ { from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad.exe')), to: '.' }, - { from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad-problem-report.exe')), to: '.' }, + { + from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad-problem-report.exe')), + to: '.', + }, { from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad-daemon.exe')), to: '.' }, { from: distAssets(path.join(getWindowsDistSubdir(), 'talpid_openvpn_plugin.dll')), to: '.' }, { - from: root(path.join('windows', 'winfw', 'bin', getWindowsTargetArch() + '-${env.CPP_BUILD_MODE}', 'winfw.dll')), + from: root( + path.join( + 'windows', + 'winfw', + 'bin', + getWindowsTargetArch() + '-${env.CPP_BUILD_MODE}', + 'winfw.dll', + ), + ), to: '.', }, // TODO: OpenVPN does not have an ARM64 build yet. { from: distAssets('binaries/x86_64-pc-windows-msvc/openvpn.exe'), to: '.' }, - { from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'apisocks5.exe')), to: '.' }, - { from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'wintun/wintun.dll')), to: '.' }, { - from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'split-tunnel/mullvad-split-tunnel.sys')), - to: '.' + from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'apisocks5.exe')), + to: '.', + }, + { + from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'wintun/wintun.dll')), + to: '.', + }, + { + from: distAssets( + path.join('binaries', getWindowsTargetSubdir(), 'split-tunnel/mullvad-split-tunnel.sys'), + ), + to: '.', }, { - from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'wireguard-nt/mullvad-wireguard.dll')), + from: distAssets( + path.join('binaries', getWindowsTargetSubdir(), 'wireguard-nt/mullvad-wireguard.dll'), + ), to: '.', }, { from: distAssets('maybenot_machines'), to: '.' }, @@ -180,7 +201,10 @@ const config = { extraResources: [ { from: distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-problem-report')), to: '.' }, { from: distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-setup')), to: '.' }, - { from: distAssets(path.join(getLinuxTargetSubdir(), 'libtalpid_openvpn_plugin.so')), to: '.' }, + { + from: distAssets(path.join(getLinuxTargetSubdir(), 'libtalpid_openvpn_plugin.so')), + to: '.', + }, { from: distAssets(path.join('linux', 'apparmor_mullvad')), to: '.' }, { from: distAssets(path.join('binaries', '${env.TARGET_TRIPLE}', 'openvpn')), to: '.' }, { from: distAssets(path.join('binaries', '${env.TARGET_TRIPLE}', 'apisocks5')), to: '.' }, @@ -197,8 +221,10 @@ const config = { distAssets('linux/before-install.sh'), '--before-remove', distAssets('linux/before-remove.sh'), - distAssets('linux/mullvad-daemon.service') +'=/usr/lib/systemd/system/mullvad-daemon.service', - distAssets('linux/mullvad-early-boot-blocking.service') +'=/usr/lib/systemd/system/mullvad-early-boot-blocking.service', + distAssets('linux/mullvad-daemon.service') + + '=/usr/lib/systemd/system/mullvad-daemon.service', + distAssets('linux/mullvad-early-boot-blocking.service') + + '=/usr/lib/systemd/system/mullvad-early-boot-blocking.service', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad')) + '=/usr/bin/', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-daemon')) + '=/usr/bin/', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-exclude')) + '=/usr/bin/', @@ -228,8 +254,10 @@ const config = { distAssets('linux/before-remove.sh'), '--rpm-posttrans', distAssets('linux/post-transaction.sh'), - distAssets('linux/mullvad-daemon.service') +'=/usr/lib/systemd/system/mullvad-daemon.service', - distAssets('linux/mullvad-early-boot-blocking.service') +'=/usr/lib/systemd/system/mullvad-early-boot-blocking.service', + distAssets('linux/mullvad-daemon.service') + + '=/usr/lib/systemd/system/mullvad-daemon.service', + distAssets('linux/mullvad-early-boot-blocking.service') + + '=/usr/lib/systemd/system/mullvad-early-boot-blocking.service', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad')) + '=/usr/bin/', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-daemon')) + '=/usr/bin/', distAssets(path.join(getLinuxTargetSubdir(), 'mullvad-exclude')) + '=/usr/bin/', @@ -265,7 +293,7 @@ function packWin() { process.env.SETUP_SUBDIR = 'aarch64-pc-windows-msvc'; break; default: - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } return true; }, @@ -279,9 +307,12 @@ function packWin() { for (const artifactPath of buildResult.artifactPaths) { const artifactDir = path.dirname(artifactPath); const artifactSemverFilename = path.basename(artifactPath); - const artifactDesiredFilename = artifactSemverFilename.replace(productSemverVersion, productTargetVersion); + const artifactDesiredFilename = artifactSemverFilename.replace( + productSemverVersion, + productTargetVersion, + ); const targetArtifactPath = path.join(artifactDir, artifactDesiredFilename); - console.log("Moving", artifactSemverFilename, "=>", artifactDesiredFilename); + console.log('Moving', artifactSemverFilename, '=>', artifactDesiredFilename); fs.renameSync(artifactPath, targetArtifactPath); } }, @@ -310,9 +341,8 @@ function packMac() { break; } - process.env.BINARIES_PATH = hostTargetTriple !== process.env.TARGET_TRIPLE - ? process.env.TARGET_TRIPLE - : ''; + process.env.BINARIES_PATH = + hostTargetTriple !== process.env.TARGET_TRIPLE ? process.env.TARGET_TRIPLE : ''; return true; }, @@ -322,8 +352,12 @@ function packMac() { // if they're present for both targets. So make sure we remove libraries for other archs. // Remove the workaround once the issue has been fixed: // https://github.com/electron/universal/issues/41#issuecomment-1496288834 - await fs.promises.rm('node_modules/nseventmonitor/lib/binding/Release', { recursive: true }); - } catch {} + await fs.promises.rm('node_modules/nseventmonitor/lib/binding/Release', { + recursive: true, + }); + } catch { + // noop + } config.beforePack?.(context); }, afterPack: (context) => { @@ -336,13 +370,15 @@ function packMac() { return Promise.resolve(); }, - afterAllArtifactBuild: async (buildResult) => { + afterAllArtifactBuild: async (_buildResult) => { // Remove the folder that contains the unpacked app. Electron builder cleans up some of // these directories and it's changed between versions without a mention in the changelog. for (const dir of appOutDirs) { try { await fs.promises.rm(dir, { recursive: true }); - } catch {} + } catch { + // noop + } } }, afterSign: (context) => { @@ -359,7 +395,7 @@ function packLinux() { } if (targets && targets === 'aarch64-unknown-linux-gnu') { - config.rpm.fpm.unshift('--architecture', 'aarch64') + config.rpm.fpm.unshift('--architecture', 'aarch64'); } return builder.build({ @@ -422,7 +458,7 @@ function getWindowsTargetArch() { if (targets === 'aarch64-pc-windows-msvc') { return 'arm64'; } - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } // Use host architecture (we assume this is x64 since building on Arm64 isn't supported). return 'x64'; @@ -433,7 +469,7 @@ function getWindowsTargetSubdir() { if (targets === 'aarch64-pc-windows-msvc') { return targets; } - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } // Use host architecture (we assume this is x64 since building on Arm64 isn't supported). return 'x86_64-pc-windows-msvc'; @@ -444,7 +480,7 @@ function getLinuxTargetArch() { if (targets === 'aarch64-unknown-linux-gnu') { return 'arm64'; } - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } // Use host architecture. return undefined; @@ -455,7 +491,7 @@ function getLinuxTargetSubdir() { if (targets === 'aarch64-unknown-linux-gnu') { return targets; } - throw new Error(`Invalid or unknown target (only one may be specified)`); + throw new Error('Invalid or unknown target (only one may be specified)'); } return ''; } @@ -489,8 +525,8 @@ function getLinuxVersion() { // Returns the product version. The `args` argument is optional. Set it to `'semver'` // to get the version in semver format. -function productVersion(extra_args) { - const args = ['run', '-q', '--bin', 'mullvad-version', ...extra_args]; +function productVersion(extraArgs) { + const args = ['run', '-q', '--bin', 'mullvad-version', ...extraArgs]; return execFileSync('cargo', args, { encoding: 'utf-8' }).trim(); } diff --git a/gui/tasks/scripts.js b/gui/tasks/scripts.js index 36b9def82de6..20acc69c35c8 100644 --- a/gui/tasks/scripts.js +++ b/gui/tasks/scripts.js @@ -37,7 +37,9 @@ function makeWatchCompiler(onFirstSuccess, onSuccess) { ) { lastBundle = bundle; lastPreloadBundle = preloadBundle; - !wasFirstBuild && onSuccess(); + if (!wasFirstBuild) { + onSuccess(); + } } }), ); diff --git a/gui/test/e2e/installed/installed-utils.ts b/gui/test/e2e/installed/installed-utils.ts index 1a8cd4258e7a..9e4dd6c79ccb 100644 --- a/gui/test/e2e/installed/installed-utils.ts +++ b/gui/test/e2e/installed/installed-utils.ts @@ -2,7 +2,7 @@ import { startApp } from '../utils'; export const startInstalledApp = async (): ReturnType => { return startApp({ executablePath: getAppInstallPath() }); -} +}; function getAppInstallPath(): string { switch (process.platform) { diff --git a/gui/test/e2e/installed/playwright.config.ts b/gui/test/e2e/installed/playwright.config.ts index 9c1fa7fb87e2..9487e06905b6 100644 --- a/gui/test/e2e/installed/playwright.config.ts +++ b/gui/test/e2e/installed/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from "@playwright/test"; +import { defineConfig } from '@playwright/test'; export default defineConfig({ testDir: process.cwd(), diff --git a/gui/test/e2e/installed/state-dependent/api-access-methods.spec.ts b/gui/test/e2e/installed/state-dependent/api-access-methods.spec.ts index 0d6e8a067208..d17611261b64 100644 --- a/gui/test/e2e/installed/state-dependent/api-access-methods.spec.ts +++ b/gui/test/e2e/installed/state-dependent/api-access-methods.spec.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged in and only have "Direct" and "Mullvad Bridges" // access methods. @@ -31,10 +31,10 @@ test.afterAll(async () => { }); async function navigateToAccessMethods() { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); - await util.waitForNavigation(async () => await page.getByText('API access').click()); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + await util.waitForNavigation(() => page.getByText('API access').click()); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('API access'); } @@ -52,9 +52,9 @@ test('App should display access methods', async () => { }); test('App should add invalid access method', async () => { - await util.waitForNavigation(async () => await page.locator('button:has-text("Add")').click()); + await util.waitForNavigation(() => page.locator('button:has-text("Add")').click()); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Add method'); const inputs = page.locator('input'); @@ -71,13 +71,13 @@ test('App should add invalid access method', async () => { await inputs.nth(2).fill(process.env.SHADOWSOCKS_SERVER_PORT!); await expect(addButton).toBeEnabled(); - await addButton.click() + await addButton.click(); await expect(page.getByText('Testing method...')).toBeVisible(); await expect(page.getByText('API unreachable, add anyway?')).toBeVisible(); expect( - await util.waitForNavigation(async () => await page.locator('button:has-text("Save")').click()) + await util.waitForNavigation(() => page.locator('button:has-text("Save")').click()), ).toEqual(RoutePath.apiAccessMethods); const accessMethods = page.getByTestId('access-method'); @@ -107,7 +107,7 @@ test('App should edit access method', async () => { await customMethod.locator('button').last().click(); await util.waitForNavigation(() => customMethod.getByText('Edit').click()); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Edit method'); const inputs = page.locator('input'); @@ -125,11 +125,13 @@ test('App should edit access method', async () => { await inputs.nth(3).fill(process.env.SHADOWSOCKS_SERVER_PASSWORD!); await page.getByTestId('ciphers').click(); - await page.getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }).click(); + await page + .getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }) + .click(); - expect( - await util.waitForNavigation(async () => await saveButton.click()) - ).toEqual(RoutePath.apiAccessMethods); + expect(await util.waitForNavigation(() => saveButton.click())).toEqual( + RoutePath.apiAccessMethods, + ); const accessMethods = page.getByTestId('access-method'); await expect(accessMethods).toHaveCount(3); diff --git a/gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts b/gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts index 5efd110213c8..34efb60f268b 100644 --- a/gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts +++ b/gui/test/e2e/installed/state-dependent/custom-bridge.spec.ts @@ -1,10 +1,10 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { colors } from '../../../../src/config.json'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged in and not have a custom bridge configured. // Env parameters: @@ -25,37 +25,35 @@ test.afterAll(async () => { }); test('App should enable bridge mode', async () => { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); - expect( - await util.waitForNavigation(async () => await page.getByText('VPN settings').click()), - ).toBe(RoutePath.vpnSettings); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + expect(await util.waitForNavigation(() => page.getByText('VPN settings').click())).toBe( + RoutePath.vpnSettings, + ); await page.getByRole('option', { name: 'OpenVPN' }).click(); - expect( - await util.waitForNavigation(async () => await page.getByText('OpenVPN settings').click()), - ).toBe(RoutePath.openVpnSettings); + expect(await util.waitForNavigation(() => page.getByText('OpenVPN settings').click())).toBe( + RoutePath.openVpnSettings, + ); await page.getByTestId('bridge-mode-on').click(); await expect(page.getByText('Enable bridge mode?')).toBeVisible(); - page.getByTestId('enable-confirm').click(); + await page.getByTestId('enable-confirm').click(); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Close"]')), - ).toBe(RoutePath.main); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Close"]'))).toBe( + RoutePath.main, + ); }); test('App display disabled custom bridge', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label^="Select location"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label^="Select location"]')), ).toBe(RoutePath.selectLocation); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Select location'); await page.getByText(/^Entry$/).click(); @@ -66,12 +64,10 @@ test('App display disabled custom bridge', async () => { test('App should add new custom bridge', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Add new custom bridge"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Add new custom bridge"]')), ).toBe(RoutePath.editCustomBridge); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Add custom bridge'); const inputs = page.locator('input'); @@ -88,11 +84,11 @@ test('App should add new custom bridge', async () => { await inputs.nth(2).fill(process.env.SHADOWSOCKS_SERVER_PASSWORD!); await page.getByTestId('ciphers').click(); - await page.getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }).click(); + await page + .getByRole('option', { name: process.env.SHADOWSOCKS_SERVER_CIPHER!, exact: true }) + .click(); - expect( - await util.waitForNavigation(async () => await addButton.click()) - ).toEqual(RoutePath.selectLocation); + expect(await util.waitForNavigation(() => addButton.click())).toEqual(RoutePath.selectLocation); const customBridgeButton = page.getByText('Custom bridge'); await expect(customBridgeButton).toBeEnabled(); @@ -109,11 +105,9 @@ test('App should select custom bridge', async () => { await page.getByText(/^Entry$/).click(); await expect(customBridgeButton).not.toHaveCSS('background-color', colors.green); - await customBridgeButton.click(); await page.getByText(/^Entry$/).click(); await expect(customBridgeButton).toHaveCSS('background-color', colors.green); - }); test('App should edit custom bridge', async () => { @@ -122,12 +116,10 @@ test('App should edit custom bridge', async () => { await page.getByText(/^Entry$/).click(); expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Edit custom bridge"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Edit custom bridge"]')), ).toBe(RoutePath.editCustomBridge); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Edit custom bridge'); const inputs = page.locator('input'); @@ -138,10 +130,7 @@ test('App should edit custom bridge', async () => { await inputs.nth(1).fill(process.env.SHADOWSOCKS_SERVER_PORT!); await expect(saveButton).toBeEnabled(); - - expect( - await util.waitForNavigation(async () => await saveButton.click()) - ).toEqual(RoutePath.selectLocation); + expect(await util.waitForNavigation(() => saveButton.click())).toEqual(RoutePath.selectLocation); const customBridgeButton = page.locator('button:has-text("Custom bridge")'); await expect(customBridgeButton).toBeEnabled(); @@ -150,9 +139,7 @@ test('App should edit custom bridge', async () => { test('App should delete custom bridge', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Edit custom bridge"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Edit custom bridge"]')), ).toBe(RoutePath.editCustomBridge); const deleteButton = page.locator('button:has-text("Delete")'); @@ -163,9 +150,9 @@ test('App should delete custom bridge', async () => { await expect(page.getByText('Delete custom bridge?')).toBeVisible(); const confirmButton = page.getByTestId('delete-confirm'); - expect( - await util.waitForNavigation(async () => await confirmButton.click()) - ).toEqual(RoutePath.selectLocation); + expect(await util.waitForNavigation(() => confirmButton.click())).toEqual( + RoutePath.selectLocation, + ); const customBridgeButton = page.locator('button:has-text("Custom bridge")'); await expect(customBridgeButton).toBeDisabled(); diff --git a/gui/test/e2e/installed/state-dependent/device-revoked.spec.ts b/gui/test/e2e/installed/state-dependent/device-revoked.spec.ts index 22aa30b3d593..2b9f8d0c586d 100644 --- a/gui/test/e2e/installed/state-dependent/device-revoked.spec.ts +++ b/gui/test/e2e/installed/state-dependent/device-revoked.spec.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; + import { RoutePath } from '../../../../src/renderer/lib/routes'; import { TestUtils } from '../../utils'; - import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged in to a revoked device. @@ -23,7 +23,7 @@ test('App should fail to login', async () => { await expect(page.getByTestId('title')).toHaveText('Device is inactive'); - expect(await util.waitForNavigation(() => { - page.getByText('Go to login').click(); - })).toEqual(RoutePath.login); + expect(await util.waitForNavigation(() => page.getByText('Go to login').click())).toEqual( + RoutePath.login, + ); }); diff --git a/gui/test/e2e/installed/state-dependent/disconnected.spec.ts b/gui/test/e2e/installed/state-dependent/disconnected.spec.ts index 360a88338d12..253545dad7ea 100644 --- a/gui/test/e2e/installed/state-dependent/disconnected.spec.ts +++ b/gui/test/e2e/installed/state-dependent/disconnected.spec.ts @@ -1,7 +1,7 @@ import { test } from '@playwright/test'; import { Page } from 'playwright'; -import { expectDisconnected } from '../../shared/tunnel-state'; +import { expectDisconnected } from '../../shared/tunnel-state'; import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged into an account that has time left and to be diff --git a/gui/test/e2e/installed/state-dependent/location.spec.ts b/gui/test/e2e/installed/state-dependent/location.spec.ts index 229adb9accd5..f0bd2e11f7ae 100644 --- a/gui/test/e2e/installed/state-dependent/location.spec.ts +++ b/gui/test/e2e/installed/state-dependent/location.spec.ts @@ -20,7 +20,6 @@ test('App should have a country', async () => { await expect(countryLabel).not.toBeEmpty(); const cityLabel = page.getByTestId('city'); - const noCityLabel = await cityLabel.count() === 0; + const noCityLabel = (await cityLabel.count()) === 0; expect(noCityLabel).toBeTruthy(); }); - diff --git a/gui/test/e2e/installed/state-dependent/login.spec.ts b/gui/test/e2e/installed/state-dependent/login.spec.ts index ba72a5b4666d..ad49c88edd21 100644 --- a/gui/test/e2e/installed/state-dependent/login.spec.ts +++ b/gui/test/e2e/installed/state-dependent/login.spec.ts @@ -1,11 +1,11 @@ -import { exec, execSync } from 'child_process'; import { expect, test } from '@playwright/test'; +import { exec, execSync } from 'child_process'; import { Locator, Page } from 'playwright'; + import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { expectDisconnected } from '../../shared/tunnel-state'; import { TestUtils } from '../../utils'; - import { startInstalledApp } from '../installed-utils'; -import { expectDisconnected } from '../../shared/tunnel-state'; // This test expects the daemon to be logged out. // Env parameters: @@ -27,7 +27,7 @@ test.afterAll(async () => { test('App should fail to login', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); const loginInput = getInput(page); @@ -46,15 +46,17 @@ test('App should fail to login', async () => { test('App should create account', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); - expect(await util.waitForNavigation(async () => { - await page.getByText('Create account').click(); + expect( + await util.waitForNavigation(async () => { + await page.getByText('Create account').click(); - await expect(title).toHaveText('Account created'); - await expect(subtitle).toHaveText('Logged in'); - })).toEqual(RoutePath.expired); + await expect(title).toHaveText('Account created'); + await expect(subtitle).toHaveText('Logged in'); + }), + ).toEqual(RoutePath.expired); const outOfTimeTitle = page.getByTestId('title'); await expect(outOfTimeTitle).toHaveText('Congrats!'); @@ -65,15 +67,17 @@ test('App should create account', async () => { }); test('App should become logged out', async () => { - expect(await util.waitForNavigation(() => { - exec('mullvad account logout'); - })).toEqual(RoutePath.login); + expect( + await util.waitForNavigation(() => { + exec('mullvad account logout'); + }), + ).toEqual(RoutePath.login); }); test('App should log in', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); const loginInput = getInput(page); @@ -82,25 +86,31 @@ test('App should log in', async () => { await loginInput.fill(process.env.ACCOUNT_NUMBER!); - expect(await util.waitForNavigation(async () => { - await loginInput.press('Enter'); + expect( + await util.waitForNavigation(async () => { + await loginInput.press('Enter'); - await expect(title).toHaveText('Logged in'); - await expect(subtitle).toHaveText('Valid account number'); - })).toEqual(RoutePath.main); + await expect(title).toHaveText('Logged in'); + await expect(subtitle).toHaveText('Valid account number'); + }), + ).toEqual(RoutePath.main); await expectDisconnected(page); }); test('App should log out', async () => { - expect(await util.waitForNavigation(() => { - void page.getByTestId('account-button').click(); - })).toEqual(RoutePath.account); - - expect(await util.waitForNavigation(() => { - void page.getByText('Log out').click(); - })).toEqual(RoutePath.login); - - const title = page.locator('h1') + expect( + await util.waitForNavigation(() => { + void page.getByTestId('account-button').click(); + }), + ).toEqual(RoutePath.account); + + expect( + await util.waitForNavigation(() => { + void page.getByText('Log out').click(); + }), + ).toEqual(RoutePath.login); + + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); await expect(title).toHaveText('Login'); await expect(subtitle).toHaveText('Enter your account number'); @@ -109,7 +119,7 @@ test('App should log out', async () => { test('App should log in to expired account', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); - const title = page.locator('h1') + const title = page.locator('h1'); const subtitle = page.getByTestId('subtitle'); const loginInput = getInput(page); @@ -118,9 +128,11 @@ test('App should log in to expired account', async () => { await loginInput.fill(accountNumber); - expect(await util.waitForNavigation(async () => { - await loginInput.press('Enter'); - })).toEqual(RoutePath.expired); + expect( + await util.waitForNavigation(async () => { + await loginInput.press('Enter'); + }), + ).toEqual(RoutePath.expired); const outOfTimeTitle = page.getByTestId('title'); await expect(outOfTimeTitle).toHaveText('Out of time'); diff --git a/gui/test/e2e/installed/state-dependent/macos-split-tunneling.spec.ts b/gui/test/e2e/installed/state-dependent/macos-split-tunneling.spec.ts index 0f1680bf444c..6d49173bcd3c 100644 --- a/gui/test/e2e/installed/state-dependent/macos-split-tunneling.spec.ts +++ b/gui/test/e2e/installed/state-dependent/macos-split-tunneling.spec.ts @@ -1,10 +1,10 @@ -import { Locator, expect, test } from '@playwright/test'; -import { Page } from 'playwright'; +import { expect, Locator, test } from '@playwright/test'; import { execSync } from 'child_process'; +import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; // macOS only. This test expects the daemon to be logged in and for split tunneling to be off and // have no split applications. @@ -21,13 +21,13 @@ test.afterAll(async () => { }); async function navigateToSplitTunneling() { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); - expect( - await util.waitForNavigation(async () => await page.getByText('Split tunneling').click()) - ).toEqual(RoutePath.splitTunneling); + expect(await util.waitForNavigation(() => page.getByText('Split tunneling').click())).toEqual( + RoutePath.splitTunneling, + ); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Split tunneling'); } @@ -46,7 +46,7 @@ test('App should enable split tunneling', async () => { const launchPadApp = page.getByText('launchpad'); await expect(launchPadApp).not.toBeVisible(); - toggle.click(); + await toggle.click(); await expect(toggle).toBeChecked(); await expect(splitList).not.toBeVisible(); await expect(nonSplitList).toBeVisible(); @@ -133,7 +133,7 @@ test('App should disable split tunneling', async () => { const launchPadApp = page.getByText('launchpad'); await expect(launchPadApp).toBeVisible(); - toggle.click(); + await toggle.click(); await expect(toggle).not.toBeChecked(); }); @@ -148,7 +148,7 @@ async function numberOfApplicationsInList(listTestid: string) { return 0; } - return await list.locator('button').count(); + return list.locator('button').count(); } function getDaemonSplitTunnelingApplications() { @@ -157,6 +157,7 @@ function getDaemonSplitTunnelingApplications() { } function isSplitInDaemon(app: string): boolean { - return !!getDaemonSplitTunnelingApplications() - .find((splitApp) => splitApp.toLowerCase().includes(app)); + return !!getDaemonSplitTunnelingApplications().find((splitApp) => + splitApp.toLowerCase().includes(app), + ); } diff --git a/gui/test/e2e/installed/state-dependent/obfuscation.spec.ts b/gui/test/e2e/installed/state-dependent/obfuscation.spec.ts index af72ded6d510..b9518f87177a 100644 --- a/gui/test/e2e/installed/state-dependent/obfuscation.spec.ts +++ b/gui/test/e2e/installed/state-dependent/obfuscation.spec.ts @@ -2,10 +2,10 @@ import { expect, test } from '@playwright/test'; import { execSync } from 'child_process'; import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { colors } from '../../../../src/config.json'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; const SHADOWSOCKS_PORT = 65_000; const UDPOVERTCP_PORT = '80'; @@ -25,14 +25,14 @@ test.afterAll(async () => { }); test('App should have automatic obfuscation', async () => { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); - expect( - await util.waitForNavigation(async () => await page.getByText('VPN settings').click()), - ).toBe(RoutePath.vpnSettings); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + expect(await util.waitForNavigation(() => page.getByText('VPN settings').click())).toBe( + RoutePath.vpnSettings, + ); - expect( - await util.waitForNavigation(async () => await page.getByText('WireGuard settings').click()), - ).toBe(RoutePath.wireguardSettings); + expect(await util.waitForNavigation(() => page.getByText('WireGuard settings').click())).toBe( + RoutePath.wireguardSettings, + ); const automatic = page.getByTestId('automatic-obfuscation'); await expect(automatic).toHaveCSS('background-color', colors.green); @@ -45,9 +45,7 @@ test('App should have automatic obfuscation', async () => { test('App should set obfuscation to shadowsocks with custom port', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Shadowsocks settings"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Shadowsocks settings"]')), ).toBe(RoutePath.shadowsocks); const automatic = page.locator('button', { hasText: 'Automatic' }); @@ -61,7 +59,7 @@ test('App should set obfuscation to shadowsocks with custom port', async () => { const customItem = page.locator('div[role="option"]', { hasText: 'Custom' }); await expect(customItem).toHaveCSS('background-color', colors.green); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); const shadowsocksItem = page.locator('button', { hasText: 'Shadowsocks' }); await shadowsocksItem.click(); @@ -74,22 +72,18 @@ test('App should set obfuscation to shadowsocks with custom port', async () => { test('App should still have shadowsocks custom port', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="Shadowsocks settings"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="Shadowsocks settings"]')), ).toBe(RoutePath.shadowsocks); const customItem = page.locator('div[role="option"]', { hasText: 'Custom' }); await expect(customItem).toHaveCSS('background-color', colors.green); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); }); test('App should set obfuscation to UDP-over-TCP with port', async () => { expect( - await util.waitForNavigation( - async () => await page.click('button[aria-label="UDP-over-TCP settings"]'), - ), + await util.waitForNavigation(() => page.click('button[aria-label="UDP-over-TCP settings"]')), ).toBe(RoutePath.udpOverTcp); const automatic = page.locator('button', { hasText: 'Automatic' }); @@ -100,7 +94,7 @@ test('App should set obfuscation to UDP-over-TCP with port', async () => { await expect(portButton).toHaveCSS('background-color', colors.green); - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')); + await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); const udpOverTcpItem = page.locator('button', { hasText: 'UDP-over-TCP' }); await udpOverTcpItem.click(); diff --git a/gui/test/e2e/installed/state-dependent/settings-import.spec.ts b/gui/test/e2e/installed/state-dependent/settings-import.spec.ts index 6b37b36244ed..d9a8859f76f9 100644 --- a/gui/test/e2e/installed/state-dependent/settings-import.spec.ts +++ b/gui/test/e2e/installed/state-dependent/settings-import.spec.ts @@ -1,9 +1,9 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { startInstalledApp } from '../installed-utils'; -import { TestUtils } from '../../utils'; import { RoutePath } from '../../../../src/renderer/lib/routes'; +import { TestUtils } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; const INVALID_JSON = 'invalid json'; const VALID_JSON = ` @@ -31,14 +31,14 @@ test.afterAll(async () => { }); async function navigateToSettingsImport() { - await util.waitForNavigation(async () => await page.click('button[aria-label="Settings"]')); - await util.waitForNavigation(async () => await page.getByText('VPN settings').click()); + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + await util.waitForNavigation(() => page.getByText('VPN settings').click()); - expect( - await util.waitForNavigation(async () => await page.getByText('Server IP override').click()) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.getByText('Server IP override').click())).toEqual( + RoutePath.settingsImport, + ); - const title = page.locator('h1') + const title = page.locator('h1'); await expect(title).toHaveText('Server IP override'); } @@ -49,14 +49,14 @@ test('App should display no overrides', async () => { }); test('App should fail to import text', async () => { - expect( - await util.waitForNavigation(async () => await page.getByText('Import via text').click()) - ).toEqual(RoutePath.settingsTextImport); + expect(await util.waitForNavigation(() => page.getByText('Import via text').click())).toEqual( + RoutePath.settingsTextImport, + ); await page.locator('textarea').fill(INVALID_JSON); - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Save"]')) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Save"]'))).toEqual( + RoutePath.settingsImport, + ); await expect(page.getByTestId('status-title')).toHaveText('NO OVERRIDES IMPORTED'); await expect(page.getByTestId('status-subtitle')).toBeVisible(); @@ -65,16 +65,16 @@ test('App should fail to import text', async () => { }); test('App should succeed to import text', async () => { - expect( - await util.waitForNavigation(async () => await page.getByText('Import via text').click()) - ).toEqual(RoutePath.settingsTextImport); + expect(await util.waitForNavigation(() => page.getByText('Import via text').click())).toEqual( + RoutePath.settingsTextImport, + ); const textarea = page.locator('textarea'); await expect(textarea).toHaveValue(INVALID_JSON); await textarea.fill(VALID_JSON); - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Save"]')) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Save"]'))).toEqual( + RoutePath.settingsImport, + ); await expect(page.getByTestId('status-title')).toHaveText('IMPORT SUCCESSFUL'); await expect(page.getByTestId('status-subtitle')).toBeVisible(); @@ -83,24 +83,24 @@ test('App should succeed to import text', async () => { await expect(page.getByTestId('status-title')).toHaveText('OVERRIDES ACTIVE'); - expect( - await util.waitForNavigation(async () => await page.getByText('Import via text').click()) - ).toEqual(RoutePath.settingsTextImport); + expect(await util.waitForNavigation(() => page.getByText('Import via text').click())).toEqual( + RoutePath.settingsTextImport, + ); await expect(textarea).toHaveValue(''); - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Close"]')) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Close"]'))).toEqual( + RoutePath.settingsImport, + ); }); test('App should show active overrides', async () => { - expect( - await util.waitForNavigation(async () => await page.click('button[aria-label="Back"]')) - ).toEqual(RoutePath.vpnSettings); - expect( - await util.waitForNavigation(async () => await page.getByText('Server IP override').click()) - ).toEqual(RoutePath.settingsImport); + expect(await util.waitForNavigation(() => page.click('button[aria-label="Back"]'))).toEqual( + RoutePath.vpnSettings, + ); + expect(await util.waitForNavigation(() => page.getByText('Server IP override').click())).toEqual( + RoutePath.settingsImport, + ); await expect(page.getByTestId('status-title')).toHaveText('OVERRIDES ACTIVE'); await expect(page.getByText('Clear all overrides')).toBeEnabled(); diff --git a/gui/test/e2e/installed/state-dependent/too-many-devices.spec.ts b/gui/test/e2e/installed/state-dependent/too-many-devices.spec.ts index 9b6dbdbe3d4b..8ff8675b6748 100644 --- a/gui/test/e2e/installed/state-dependent/too-many-devices.spec.ts +++ b/gui/test/e2e/installed/state-dependent/too-many-devices.spec.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; import { Locator, Page } from 'playwright'; + import { RoutePath } from '../../../../src/renderer/lib/routes'; import { TestUtils } from '../../utils'; - import { startInstalledApp } from '../installed-utils'; // This test expects the daemon to be logged out and the provided account to have five registered @@ -25,25 +25,26 @@ test('App should show too many devices', async () => { expect(await util.currentRoute()).toEqual(RoutePath.login); const loginInput = getInput(page); - await loginInput.type(process.env.ACCOUNT_NUMBER!); + await loginInput.fill(process.env.ACCOUNT_NUMBER!); - expect(await util.waitForNavigation(() => { - loginInput.press('Enter'); - })).toEqual(RoutePath.tooManyDevices); + expect(await util.waitForNavigation(() => loginInput.press('Enter'))).toEqual( + RoutePath.tooManyDevices, + ); const loginButton = page.getByText('Continue with login'); await expect(page.getByTestId('title')).toHaveText('Too many devices'); await expect(loginButton).toBeDisabled(); - await page.getByLabel(/^Remove device named/).first().click(); + await page + .getByLabel(/^Remove device named/) + .first() + .click(); await page.getByText('Yes, log out device').click(); await expect(loginButton).toBeEnabled(); // Trigger transition: too-many-devices -> login -> main - expect(await util.waitForNavigation(() => { - loginButton.click(); - })).toEqual(RoutePath.login); + expect(await util.waitForNavigation(() => loginButton.click())).toEqual(RoutePath.login); // Note: `util.waitForNavigation` won't return the navigation event when // transitioning from login -> main, so we need to observe the state of the diff --git a/gui/test/e2e/installed/state-dependent/tunnel-state.spec.ts b/gui/test/e2e/installed/state-dependent/tunnel-state.spec.ts index 53332ee6ab00..15ce240e577d 100644 --- a/gui/test/e2e/installed/state-dependent/tunnel-state.spec.ts +++ b/gui/test/e2e/installed/state-dependent/tunnel-state.spec.ts @@ -1,15 +1,11 @@ -import { exec as execAsync } from 'child_process'; -import { promisify } from 'util'; import { expect, test } from '@playwright/test'; +import { exec as execAsync } from 'child_process'; import { Page } from 'playwright'; -import { - expectConnected, - expectDisconnected, - expectError, -} from '../../shared/tunnel-state'; +import { promisify } from 'util'; -import { startInstalledApp } from '../installed-utils'; +import { expectConnected, expectDisconnected, expectError } from '../../shared/tunnel-state'; import { escapeRegExp } from '../../utils'; +import { startInstalledApp } from '../installed-utils'; const exec = promisify(execAsync); @@ -164,7 +160,9 @@ test('App should show multihop', async () => { await exec('mullvad relay set tunnel wireguard --use-multihop=on'); await expectConnected(page); const relay = page.getByTestId('hostname-line'); - await expect(relay).toHaveText(new RegExp('^' + escapeRegExp(`${process.env.HOSTNAME} via`), 'i')); + await expect(relay).toHaveText( + new RegExp('^' + escapeRegExp(`${process.env.HOSTNAME} via`), 'i'), + ); await exec('mullvad relay set tunnel wireguard --use-multihop=off'); await page.getByText('Disconnect').click(); }); diff --git a/gui/test/e2e/mocked/expired-account-error-view.spec.ts b/gui/test/e2e/mocked/expired-account-error-view.spec.ts index 5c906a134b35..e63e62f51f8c 100644 --- a/gui/test/e2e/mocked/expired-account-error-view.spec.ts +++ b/gui/test/e2e/mocked/expired-account-error-view.spec.ts @@ -1,10 +1,11 @@ -import { Page } from 'playwright'; -import { MockedTestUtils, startMockedApp } from './mocked-utils'; import { expect, test } from '@playwright/test'; -import { IAccountData } from '../../../src/shared/daemon-rpc-types'; -import { getBackgroundColor } from '../utils'; +import { Page } from 'playwright'; + import { colors } from '../../../src/config.json'; import { RoutePath } from '../../../src/renderer/lib/routes'; +import { IAccountData } from '../../../src/shared/daemon-rpc-types'; +import { getBackgroundColor } from '../utils'; +import { MockedTestUtils, startMockedApp } from './mocked-utils'; let page: Page; let util: MockedTestUtils; @@ -37,10 +38,12 @@ test('App should show out of time view after running out of time', async () => { const expiryDate = new Date(); expiryDate.setSeconds(expiryDate.getSeconds() + 2); - expect(await util.waitForNavigation(async () => { - await util.sendMockIpcResponse({ - channel: 'account-', - response: { expiry: expiryDate.toISOString() }, - }); - })).toEqual(RoutePath.expired); + expect( + await util.waitForNavigation(async () => { + await util.sendMockIpcResponse({ + channel: 'account-', + response: { expiry: expiryDate.toISOString() }, + }); + }), + ).toEqual(RoutePath.expired); }); diff --git a/gui/test/e2e/mocked/feature-indicators.spec.ts b/gui/test/e2e/mocked/feature-indicators.spec.ts index 1ba216bd52d5..6e0e034c352f 100644 --- a/gui/test/e2e/mocked/feature-indicators.spec.ts +++ b/gui/test/e2e/mocked/feature-indicators.spec.ts @@ -1,9 +1,14 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { MockedTestUtils, startMockedApp } from './mocked-utils'; -import { FeatureIndicator, ILocation, ITunnelEndpoint, TunnelState } from '../../../src/shared/daemon-rpc-types'; +import { + FeatureIndicator, + ILocation, + ITunnelEndpoint, + TunnelState, +} from '../../../src/shared/daemon-rpc-types'; import { expectConnected } from '../shared/tunnel-state'; +import { MockedTestUtils, startMockedApp } from './mocked-utils'; const endpoint: ITunnelEndpoint = { address: 'wg10:80', @@ -91,23 +96,23 @@ test('App should show feature indicators', async () => { await expect(ellipsis).toBeVisible(); await expectConnected(page); - await expectFeatureIndicators(page, ["DAITA", "Quantum resistance"], false); - await expectHiddenFeatureIndicator(page, "Mssfix"); + await expectFeatureIndicators(page, ['DAITA', 'Quantum resistance'], false); + await expectHiddenFeatureIndicator(page, 'Mssfix'); await page.getByTestId('connection-panel-chevron').click(); await expect(ellipsis).not.toBeVisible(); await expectFeatureIndicators(page, [ - "DAITA", - "Quantum resistance", - "Mssfix", - "MTU", - "Obfuscation", - "Local network sharing", - "Lockdown mode", - "Multihop", - "Custom DNS", - "Server IP override", + 'DAITA', + 'Quantum resistance', + 'Mssfix', + 'MTU', + 'Obfuscation', + 'Local network sharing', + 'Lockdown mode', + 'Multihop', + 'Custom DNS', + 'Server IP override', ]); }); @@ -123,11 +128,7 @@ async function expectHiddenFeatureIndicator(page: Page, hiddenIndicator: string) await expect(indicator).not.toBeVisible(); } -async function expectFeatureIndicators( - page: Page, - expectedIndicators: Array, - only = true, -) { +async function expectFeatureIndicators(page: Page, expectedIndicators: Array, only = true) { const indicators = page.getByTestId('feature-indicator'); if (only) { await expect(indicators).toHaveCount(expectedIndicators.length); diff --git a/gui/test/e2e/mocked/mocked-utils.ts b/gui/test/e2e/mocked/mocked-utils.ts index 1840ebbbb0a1..57998fae685a 100644 --- a/gui/test/e2e/mocked/mocked-utils.ts +++ b/gui/test/e2e/mocked/mocked-utils.ts @@ -3,7 +3,7 @@ import { ElectronApplication } from 'playwright'; import { startApp, TestUtils } from '../utils'; interface StartMockedAppResponse extends Awaited> { - util: MockedTestUtils, + util: MockedTestUtils; } export interface MockedTestUtils extends TestUtils { @@ -22,7 +22,7 @@ export const startMockedApp = async (): Promise => { ...startAppResult.util, mockIpcHandle, sendMockIpcResponse, - } + }, }; }; diff --git a/gui/test/e2e/mocked/settings.spec.ts b/gui/test/e2e/mocked/settings.spec.ts index c6022678f53c..52dbc72402ea 100644 --- a/gui/test/e2e/mocked/settings.spec.ts +++ b/gui/test/e2e/mocked/settings.spec.ts @@ -1,8 +1,8 @@ import { expect, test } from '@playwright/test'; import { Page } from 'playwright'; -import { MockedTestUtils, startMockedApp } from './mocked-utils'; import { IAccountData } from '../../../src/shared/daemon-rpc-types'; +import { MockedTestUtils, startMockedApp } from './mocked-utils'; let page: Page; let util: MockedTestUtils; @@ -21,7 +21,7 @@ test('Account button should be displayed correctly', async () => { }); test('Headerbar account info should be displayed correctly', async () => { - let expiryText = page.getByText(/^Time left:/); + const expiryText = page.getByText(/^Time left:/); await expect(expiryText).toContainText(/Time left: 29 days/i); /** diff --git a/gui/test/e2e/mocked/tunnel-state.spec.ts b/gui/test/e2e/mocked/tunnel-state.spec.ts index 3e20dab6ec8e..6ecf707ba607 100644 --- a/gui/test/e2e/mocked/tunnel-state.spec.ts +++ b/gui/test/e2e/mocked/tunnel-state.spec.ts @@ -1,9 +1,20 @@ import { test } from '@playwright/test'; import { Page } from 'playwright'; +import { + ErrorStateCause, + ILocation, + ITunnelEndpoint, + TunnelState, +} from '../../../src/shared/daemon-rpc-types'; +import { + expectConnected, + expectConnecting, + expectDisconnected, + expectDisconnecting, + expectError, +} from '../shared/tunnel-state'; import { MockedTestUtils, startMockedApp } from './mocked-utils'; -import { ErrorStateCause, ILocation, ITunnelEndpoint, TunnelState } from '../../../src/shared/daemon-rpc-types'; -import { expectConnected, expectConnecting, expectDisconnected, expectDisconnecting, expectError } from '../shared/tunnel-state'; const mockLocation: ILocation = { country: 'Sweden', diff --git a/gui/test/e2e/shared/tunnel-state.ts b/gui/test/e2e/shared/tunnel-state.ts index 4d75f79d01bb..6a5fbfaf0968 100644 --- a/gui/test/e2e/shared/tunnel-state.ts +++ b/gui/test/e2e/shared/tunnel-state.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test'; import { Page } from 'playwright'; + import { colors } from '../../../src/config.json'; import { anyOf } from '../utils'; diff --git a/gui/test/e2e/utils.ts b/gui/test/e2e/utils.ts index 082ccfcf3f28..2fb33319f20e 100644 --- a/gui/test/e2e/utils.ts +++ b/gui/test/e2e/utils.ts @@ -1,4 +1,4 @@ -import { Locator, Page, _electron as electron, ElectronApplication } from 'playwright'; +import { _electron as electron, ElectronApplication, Locator, Page } from 'playwright'; export interface StartAppResponse { app: ElectronApplication; @@ -8,7 +8,7 @@ export interface StartAppResponse { export interface TestUtils { currentRoute: () => Promise; - waitForNavigation: (initiateNavigation?: () => Promise | void) => Promise; + waitForNavigation: (initiateNavigation?: () => Promise | void) => Promise; waitForNoTransition: () => Promise; } @@ -38,34 +38,28 @@ export const startApp = async (options: LaunchOptions): Promise => { +export const launch = (options: LaunchOptions): Promise => { process.env.CI = 'e2e'; - return await electron.launch(options); -} + return electron.launch(options); +}; const currentRouteFactory = (app: ElectronApplication) => { - return async () => { - return await app.evaluate(({ webContents }) => { - return webContents.getAllWebContents() - // Select window that isn't devtools - .find((webContents) => webContents.getURL().startsWith('file://'))! - .executeJavaScript('window.e2e.location'); - }); - }; -} + return () => + app.evaluate(({ webContents }) => + webContents + .getAllWebContents() + // Select window that isn't devtools + .find((webContents) => webContents.getURL().startsWith('file://'))! + .executeJavaScript('window.e2e.location'), + ); +}; -const waitForNavigationFactory = ( - app: ElectronApplication, - page: Page, -) => { +const waitForNavigationFactory = (app: ElectronApplication, page: Page) => { // Wait for navigation animation to finish. A function can be provided that initiates the // navigation, e.g. clicks a button. return async (initiateNavigation?: () => Promise | void) => { // Wait for route to change after optionally initiating the navigation. - const [route] = await Promise.all([ - waitForNextRoute(app), - initiateNavigation?.(), - ]); + const [route] = await Promise.all([waitForNextRoute(app), initiateNavigation?.()]); // Wait for view corresponding to new route to appear await page.getByTestId(route).isVisible(); @@ -77,7 +71,7 @@ const waitForNavigationFactory = ( const waitForNoTransition = async (page: Page) => { // Wait until there's only one transitionContents - let transitionContentsCount; + let transitionContentsCount; do { if (transitionContentsCount !== undefined) { await new Promise((resolve) => setTimeout(resolve, 5)); @@ -93,14 +87,15 @@ const waitForNoTransition = async (page: Page) => { }; // Returns the route when it changes -const waitForNextRoute = async (app: ElectronApplication): Promise => { - return await app.evaluate(({ ipcMain }) => { - return new Promise((resolve) => { - ipcMain.once('navigation-setHistory', (_event, history: History) => { +const waitForNextRoute = (app: ElectronApplication): Promise => { + return app.evaluate( + ({ ipcMain }) => + new Promise((resolve) => { + ipcMain.once('navigation-setHistory', (_event, history: History) => { resolve(history.entries[history.index].pathname); - }); - }); - }); + }); + }), + ); }; const getStyleProperty = (locator: Locator, property: string) => { @@ -125,5 +120,5 @@ export function anyOf(...values: string[]): RegExp { } export function escapeRegExp(regexp: string): string { - return regexp.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + return regexp.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } diff --git a/gui/test/unit/account-data-cache.spec.ts b/gui/test/unit/account-data-cache.spec.ts index f3d6df3142ac..cf1c4299d843 100644 --- a/gui/test/unit/account-data-cache.spec.ts +++ b/gui/test/unit/account-data-cache.spec.ts @@ -1,7 +1,8 @@ +import { expect, spy } from 'chai'; +import sinon from 'sinon'; + import AccountDataCache, { AccountFetchError } from '../../src/main/account-data-cache'; import { AccountDataResponse, IAccountData } from '../../src/shared/daemon-rpc-types'; -import sinon from 'sinon'; -import { expect, spy } from 'chai'; describe('IAccountData cache', () => { const dummyAccountToken = '9876543210'; @@ -44,7 +45,7 @@ describe('IAccountData cache', () => { const watcher = new Promise((resolve, reject) => { cache.fetch(dummyAccountToken, { - onFinish: (_reason?: any) => resolve(), + onFinish: () => resolve(), onError: (_error: AccountFetchError) => reject(), }); }); @@ -254,9 +255,7 @@ describe('IAccountData cache', () => { await new Promise((resolve) => setTimeout(resolve)); cache.fetch(dummyAccountToken, { - onFinish: async () => { - resolve(); - }, + onFinish: () => resolve(), onError: (_error: AccountFetchError) => reject(), }); }, @@ -298,9 +297,7 @@ describe('IAccountData cache', () => { await new Promise((resolve) => setTimeout(resolve)); cache.fetch(dummyAccountToken, { - onFinish: async () => { - resolve(); - }, + onFinish: () => resolve(), onError: (_error: AccountFetchError) => reject(), }); }, diff --git a/gui/test/unit/changelog.spec.ts b/gui/test/unit/changelog.spec.ts index 249c26e1db67..774dca956bb0 100644 --- a/gui/test/unit/changelog.spec.ts +++ b/gui/test/unit/changelog.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { after, it, describe } from 'mocha'; +import { after, describe, it } from 'mocha'; + import { parseChangelog } from '../../src/main/changelog'; // It should be handled the same no matter if the platforms are split with a space or not. diff --git a/gui/test/unit/date-helper.spec.ts b/gui/test/unit/date-helper.spec.ts index 74500ff9606d..82c4faeac78d 100644 --- a/gui/test/unit/date-helper.spec.ts +++ b/gui/test/unit/date-helper.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; + import * as date from '../../src/shared/date-helper'; describe('Date helper', () => { @@ -100,76 +101,61 @@ describe('Date helper', () => { }); it('should format positive difference as string', () => { - const diff1 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-01-01 13:37:20', - { displayMonths: true }, - ); + const diff1 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-01-01 13:37:20', { + displayMonths: true, + }); expect(diff1).to.equal('less than a day'); - const diff2 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-01-02 13:37:20', - { displayMonths: true }, - ); + const diff2 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-01-02 13:37:20', { + displayMonths: true, + }); expect(diff2).to.equal('1 day'); - const diff3 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-02-25 13:37:20', - { displayMonths: true }, - ); + const diff3 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-02-25 13:37:20', { + displayMonths: true, + }); expect(diff3).to.equal('55 days'); - const diff4 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-04-25 13:37:20', - { displayMonths: true }, - ); + const diff4 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-04-25 13:37:20', { + displayMonths: true, + }); expect(diff4).to.equal('3 months'); - const diff5 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2031-04-25 13:37:20', - { displayMonths: true }, - ); + const diff5 = date.formatRelativeDate('2021-01-01 13:37:10', '2031-04-25 13:37:20', { + displayMonths: true, + }); expect(diff5).to.equal('10 years'); }); it('should format positive difference as string suffixed with "left"', () => { - const diff1 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-01-01 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff1 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-01-01 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff1).to.equal('less than a day left'); - const diff2 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-01-02 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff2 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-01-02 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff2).to.equal('1 day left'); - const diff3 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-02-25 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff3 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-02-25 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff3).to.equal('55 days left'); - const diff4 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2021-04-25 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff4 = date.formatRelativeDate('2021-01-01 13:37:10', '2021-04-25 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff4).to.equal('3 months left'); - const diff5 = date.formatRelativeDate( - '2021-01-01 13:37:10', - '2031-04-25 13:37:20', - { suffix: true, displayMonths: true }, - ); + const diff5 = date.formatRelativeDate('2021-01-01 13:37:10', '2031-04-25 13:37:20', { + suffix: true, + displayMonths: true, + }); expect(diff5).to.equal('10 years left'); }); diff --git a/gui/test/unit/history.spec.ts b/gui/test/unit/history.spec.ts index 03eabd80cc2c..739c65c5caa7 100644 --- a/gui/test/unit/history.spec.ts +++ b/gui/test/unit/history.spec.ts @@ -1,5 +1,6 @@ import { expect, spy } from 'chai'; -import { it, describe, beforeEach } from 'mocha'; +import { beforeEach, describe, it } from 'mocha'; + import History from '../../src/renderer/lib/history'; import { RoutePath } from '../../src/renderer/lib/routes'; diff --git a/gui/test/unit/ip.spec.ts b/gui/test/unit/ip.spec.ts index 10b93bc89099..5f6a265d253a 100644 --- a/gui/test/unit/ip.spec.ts +++ b/gui/test/unit/ip.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; + import * as ip from '../../src/renderer/lib/ip'; const validIpv4Addresses = [ diff --git a/gui/test/unit/keyframe-animation.spec.ts b/gui/test/unit/keyframe-animation.spec.ts index 59b5c2d6f723..0ba302e65c76 100644 --- a/gui/test/unit/keyframe-animation.spec.ts +++ b/gui/test/unit/keyframe-animation.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; + import KeyframeAnimation from '../../src/main/keyframe-animation'; describe('lib/keyframe-animation', function () { diff --git a/gui/test/unit/list-diff.spec.ts b/gui/test/unit/list-diff.spec.ts index 93575790720f..e64d79b19339 100644 --- a/gui/test/unit/list-diff.spec.ts +++ b/gui/test/unit/list-diff.spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; + import { calculateItemList, RowDisplayData } from '../../src/renderer/components/List'; const prevItems: Array> = [ diff --git a/gui/test/unit/logging.spec.ts b/gui/test/unit/logging.spec.ts index d107a23d3b67..393b631ba5d3 100644 --- a/gui/test/unit/logging.spec.ts +++ b/gui/test/unit/logging.spec.ts @@ -1,10 +1,11 @@ import { expect, spy } from 'chai'; import fs from 'fs'; -import sinon from 'sinon'; -import { it, describe, before, beforeEach, after } from 'mocha'; +import { after, before, beforeEach, describe, it } from 'mocha'; import path from 'path'; -import { Logger } from '../../src/shared/logging'; +import sinon from 'sinon'; + import { backupLogFile, rotateOrDeleteFile } from '../../src/main/logging'; +import { Logger } from '../../src/shared/logging'; import { LogLevel } from '../../src/shared/logging-types'; const aPath = path.normalize('log-directory/a.log'); diff --git a/gui/test/unit/notification-evaluation.spec.ts b/gui/test/unit/notification-evaluation.spec.ts index 27de83cf2984..723307b3d776 100644 --- a/gui/test/unit/notification-evaluation.spec.ts +++ b/gui/test/unit/notification-evaluation.spec.ts @@ -1,21 +1,25 @@ import { expect } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; import sinon from 'sinon'; -import { - UnsupportedVersionNotificationProvider, - UpdateAvailableNotificationProvider, -} from '../../src/shared/notifications/notification'; import NotificationController from '../../src/main/notification-controller'; import { TunnelState } from '../../src/shared/daemon-rpc-types'; import { ErrorStateCause } from '../../src/shared/daemon-rpc-types'; import { FirewallPolicyErrorType } from '../../src/shared/daemon-rpc-types'; +import { + UnsupportedVersionNotificationProvider, + UpdateAvailableNotificationProvider, +} from '../../src/shared/notifications/notification'; function createController() { return new NotificationController({ - openApp: () => { /* no-op */ }, + openApp: () => { + /* no-op */ + }, openLink: (_url: string, _withAuth?: boolean) => Promise.resolve(), - showNotificationIcon: (_value: boolean) => { /* no-op */ }, + showNotificationIcon: (_value: boolean) => { + /* no-op */ + }, }); } @@ -26,10 +30,18 @@ describe('System notifications', () => { sandbox = sinon.createSandbox(); // @ts-ignore sandbox.stub(NotificationController.prototype, 'createElectronNotification').returns({ - show: () => { /* no-op */ }, - close: () => { /* no-op */ }, - on: () => { /* no-op */ }, - removeAllListeners: () => { /* no-op */ }, + show: () => { + /* no-op */ + }, + close: () => { + /* no-op */ + }, + on: () => { + /* no-op */ + }, + removeAllListeners: () => { + /* no-op */ + }, }); }); @@ -133,7 +145,7 @@ describe('System notifications', () => { cause: ErrorStateCause.isOffline, blockingError: { type: FirewallPolicyErrorType.generic, - } + }, }, }; const result6 = controller.notifyTunnelState(nonBlockingErrorState, false, false, false, false); diff --git a/gui/test/unit/setup.ts b/gui/test/unit/setup.ts index 65644e28d98c..a565ec50ca23 100644 --- a/gui/test/unit/setup.ts +++ b/gui/test/unit/setup.ts @@ -1,6 +1,6 @@ import chai from 'chai'; -import spies from 'chai-spies'; import chaiAsPromised from 'chai-as-promised'; +import spies from 'chai-spies'; chai.use(spies); chai.use(chaiAsPromised); diff --git a/gui/test/unit/tunnel-state.spec.ts b/gui/test/unit/tunnel-state.spec.ts index d4629ebc64b9..a6013523b843 100644 --- a/gui/test/unit/tunnel-state.spec.ts +++ b/gui/test/unit/tunnel-state.spec.ts @@ -1,6 +1,7 @@ import { expect, spy } from 'chai'; -import { it, describe } from 'mocha'; +import { describe, it } from 'mocha'; import sinon from 'sinon'; + import TunnelStateHandler from '../../src/main/tunnel-state'; import { TunnelState } from '../../src/shared/daemon-rpc-types'; @@ -106,4 +107,3 @@ describe('Tunnel state', () => { clock.restore(); }); }); -