diff options
| -rw-r--r-- | desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade.spec.ts | 569 |
1 files changed, 278 insertions, 291 deletions
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade.spec.ts index 3d2817ee71..0bd6f51ef6 100644 --- a/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade.spec.ts +++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade.spec.ts @@ -8,354 +8,341 @@ import { MockedTestUtils, startMockedApp } from './mocked-utils'; let page: Page; let util: MockedTestUtils; -test.beforeAll(async () => { - ({ page, util } = await startMockedApp()); +test.describe('App upgrade', () => { + const mockedVersion = '2100.1'; + const mockedChangelog = [ + 'This is a changelog.', + 'Each item is on a separate line.', + 'There are three items.', + ]; - await util.sendMockIpcResponse<IAppVersionInfo>({ - channel: 'upgradeVersion-', - response: { - supported: true, - suggestedIsBeta: false, - suggestedUpgrade: { - version: '2100.1', - changelog: [ - 'This is a changelog.', - 'Each item is on a separate line.', - 'There are three items.', - ], - }, - }, - }); -}); + test.beforeAll(async () => { + ({ page, util } = await startMockedApp()); -test.afterEach(async () => { - await new Promise((resolve) => { - setTimeout(() => { - resolve(null); - }, 5000); - }); -}); - -test.afterAll(async () => { - await page.close(); -}); - -test('App should navigate to App upgrade view', async () => { - await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); - await util.waitForNavigation(() => page.getByRole('button', { name: 'App info' }).click()); - await util.waitForNavigation(() => - page.getByRole('button', { name: 'Update available' }).click(), - ); -}); - -test('App should display version and changelog of new upgrade', async () => { - const headingText = await page - .getByRole('heading', { - name: 'Version', - }) - .textContent(); - expect(headingText).toBe('Version 2100.1'); - - const changelogList = page.getByRole('list'); - const changelogListText = await changelogList.textContent(); - expect(changelogListText).toEqual( - 'This is a changelog.Each item is on a separate line.There are three items.', - ); - - const changelogListItems = page.getByRole('listitem'); - const changelogListItemsCount = await changelogListItems.count(); - expect(changelogListItemsCount).toBe(3); -}); + await util.sendMockIpcResponse<IAppVersionInfo>({ + channel: 'upgradeVersion-', + response: { + supported: true, + suggestedIsBeta: false, + suggestedUpgrade: { + version: mockedVersion, + changelog: mockedChangelog, + }, + }, + }); -test('App should start upgrade when clicking Download & install button', async () => { - const downloadAndInstallButton = page.getByRole('button', { - name: 'Download and install', + await util.waitForNavigation(() => page.click('button[aria-label="Settings"]')); + await util.waitForNavigation(() => page.getByRole('button', { name: 'App info' }).click()); + await util.waitForNavigation(() => + page.getByRole('button', { name: 'Update available' }).click(), + ); }); - const appUpgradePromise = util.mockIpcHandle({ - channel: 'appUpgrade', - response: undefined, + test.afterAll(async () => { + await page.close(); }); - await downloadAndInstallButton.click(); + const resolveIpcHandle = async (test: Promise<void>, trigger: Promise<void>) => { + // The promise is resolved when its handle has been called. + // The handle should be called when the trigger is called. + const promise = await Promise.all([test, trigger]); + expect(promise).toBeTruthy(); + }; - // The appUpgrade promise is resolved when its handle has been called. - // The handle should be called when the Download & install button is clicked. - await appUpgradePromise; + const selectors = { + downloadAndInstallButton: () => + page.getByRole('button', { + name: 'Download and install', + }), + installButton: () => + page.getByRole('button', { + name: 'Install update', + }), + cancelButton: () => + page.getByRole('button', { + name: 'Cancel', + }), + retryButton: () => + page.getByRole('button', { + name: 'Retry download', + }), + reportProblemButton: () => + page.getByRole('button', { + name: 'Report a problem', + }), + downloadProgressBar: () => page.getByRole('progressbar'), + } as const; - // Mock that we have started downloading the upgrade - await util.sendMockIpcResponse<AppUpgradeEvent>({ - channel: 'app-upgradeEvent', - response: { - type: 'APP_UPGRADE_STATUS_DOWNLOAD_STARTED', - }, - }); + const startAppUpgrade = async () => { + const downloadAndInstallButton = selectors.downloadAndInstallButton(); - await expect(downloadAndInstallButton).toBeHidden(); -}); + await resolveIpcHandle( + util.mockIpcHandle({ + channel: 'appUpgrade', + response: undefined, + }), + downloadAndInstallButton.click(), + ); -test('App should show Cancel button after upgrade started', async () => { - const cancelButton = page.getByRole('button', { - name: 'Cancel', - }); - await expect(cancelButton).toBeVisible(); - await expect(cancelButton).toBeEnabled(); -}); + // Mock that we have started downloading the upgrade + await util.sendMockIpcResponse<AppUpgradeEvent>({ + channel: 'app-upgradeEvent', + response: { + type: 'APP_UPGRADE_STATUS_DOWNLOAD_STARTED', + }, + }); + }; -test('App should show indeterminate download progress after upgrade started', async () => { - // TODO: Improve by using aria labels - await expect(page.getByText('Downloading...')).toBeVisible(); - await expect(page.getByText('Starting download...')).toBeVisible(); - await expect(page.getByText('0%')).toBeVisible(); + const expectProgress = async (progress: number, expectLabel?: boolean) => { + if (expectLabel) { + await expect(page.getByText(`${progress}%`)).toBeVisible(); + } - const downloadProgressBarValue = await page - .getByRole('progressbar') - .getAttribute('aria-valuenow'); - expect(downloadProgressBarValue).toEqual('0'); -}); + const downloadProgressBarValue = await selectors + .downloadProgressBar() + .getAttribute('aria-valuenow'); + expect(downloadProgressBarValue).toEqual(progress.toString()); + }; -test('App should show download progress after receiving event', async () => { - // Mock that we have started downloading the upgrade - await util.sendMockIpcResponse<AppUpgradeEvent>({ - channel: 'app-upgradeEvent', - response: { - type: 'APP_UPGRADE_STATUS_DOWNLOAD_PROGRESS', - progress: 90, - server: 'cdn.mullvad.net', - timeLeft: 120, - }, - }); - - // TODO: Improve by using aria labels - await expect(page.getByText('Downloading from: cdn.mullvad.net')).toBeVisible(); - await expect(page.getByText('About 2 minutes remaining...')).toBeVisible(); + test.describe('Should display changelog', () => { + test('Should display new version number as heading', async () => { + const headingText = await page + .getByRole('heading', { + name: 'Version', + }) + .textContent(); + expect(headingText).toBe(`Version ${mockedVersion}`); + }); - const downloadProgressBarValue = await page - .getByRole('progressbar') - .getAttribute('aria-valuenow'); - expect(downloadProgressBarValue).toEqual('90'); -}); + test('Should display new version changelog', async () => { + const changelogList = page.getByRole('list'); + const changelogListText = await changelogList.textContent(); + expect(changelogListText).toEqual(mockedChangelog.join('')); -test('App should cancel upgrade when clicking the Cancel button', async () => { - const cancelButton = page.getByRole('button', { - name: 'Cancel', + const changelogListItems = page.getByRole('listitem'); + const changelogListItemsCount = await changelogListItems.count(); + expect(changelogListItemsCount).toBe(mockedChangelog.length); + }); }); - const appUpgradeAbortPromise = util.mockIpcHandle({ - channel: 'appUpgradeAbort', - response: undefined, - }); + test.describe('Should download upgrade', () => { + test('Should start upgrade when clicking Download & install button', async () => { + await startAppUpgrade(); + const downloadAndInstallButton = selectors.downloadAndInstallButton(); + await expect(downloadAndInstallButton).toBeHidden(); + }); - await cancelButton.click(); + test('Should show indeterminate download progress after upgrade started', async () => { + // TODO: Improve by using aria labels + await expect(page.getByText('Downloading...')).toBeVisible(); + await expect(page.getByText('Starting download...')).toBeVisible(); - // The appUpgradeAbort promise is resolved when its handle has been called. - // The handle should be called when the cancel button is clicked. - await appUpgradeAbortPromise; + await expectProgress(0, true); + }); - // After the app upgrade abort RPC is sent we expect to receive an aborted - // event. - await util.sendMockIpcResponse<AppUpgradeEvent>({ - channel: 'app-upgradeEvent', - response: { - type: 'APP_UPGRADE_STATUS_ABORTED', - }, - }); + test('Should show download progress after receiving event', async () => { + const mockedProgress = 90; + // Mock that we have started downloading the upgrade + await util.sendMockIpcResponse<AppUpgradeEvent>({ + channel: 'app-upgradeEvent', + response: { + type: 'APP_UPGRADE_STATUS_DOWNLOAD_PROGRESS', + progress: mockedProgress, + server: 'cdn.mullvad.net', + timeLeft: 120, + }, + }); - // The cancel button should be hidden when the upgrade is aborted - await expect(cancelButton).toBeHidden(); + await expect(page.getByText('Downloading from: cdn.mullvad.net')).toBeVisible(); + await expect(page.getByText('About 2 minutes remaining...')).toBeVisible(); - // The Download & install button should become visible again - const downloadAndInstallButton = page.getByRole('button', { - name: 'Download and install', - }); - await expect(downloadAndInstallButton).toBeVisible(); -}); + await expectProgress(mockedProgress, true); + }); -test('App should start upgrade again when clicking Download & install button', async () => { - const downloadAndInstallButton = page.getByRole('button', { - name: 'Download and install', - }); + test('Should verify installer when download is complete', async () => { + // Mock that we have started verifying the upgrade + await util.sendMockIpcResponse<AppUpgradeEvent>({ + channel: 'app-upgradeEvent', + response: { + type: 'APP_UPGRADE_STATUS_VERIFYING_INSTALLER', + }, + }); - const appUpgradePromise = util.mockIpcHandle({ - channel: 'appUpgrade', - response: undefined, - }); + await expect(page.getByText('Verifying installer')).toBeVisible(); + await expect(page.getByText('Download complete')).toBeVisible(); - await downloadAndInstallButton.click(); + await expectProgress(100); + }); - // The appUpgrade promise is resolved when its handle has been called. - // The handle should be called when the Download & install button is clicked. - await appUpgradePromise; + test('Should show that it has verified the installer when verification is complete', async () => { + // Mock that we have verified the upgrade + await util.sendMockIpcResponse<AppUpgradeEvent>({ + channel: 'app-upgradeEvent', + response: { + type: 'APP_UPGRADE_STATUS_VERIFIED_INSTALLER', + }, + }); - // Mock that we have started downloading the upgrade - await util.sendMockIpcResponse<AppUpgradeEvent>({ - channel: 'app-upgradeEvent', - response: { - type: 'APP_UPGRADE_STATUS_DOWNLOAD_STARTED', - }, - }); + await expect(page.getByText('Verification successful! Starting installer...')).toBeVisible(); + await expect(page.getByText('Download complete')).toBeVisible(); - await expect(downloadAndInstallButton).toBeHidden(); -}); - -test('App should show that it is verifying the installer when download is complete', async () => { - // Mock that we have started verifying the upgrade - await util.sendMockIpcResponse<AppUpgradeEvent>({ - channel: 'app-upgradeEvent', - response: { - type: 'APP_UPGRADE_STATUS_VERIFYING_INSTALLER', - }, + await expectProgress(100); + }); }); - // TODO: Improve by using aria labels - await expect(page.getByText('Verifying installer')).toBeVisible(); - await expect(page.getByText('Download complete')).toBeVisible(); + test.describe('Should handle failing to download upgrade', () => { + test('Should handle failing to download upgrade', async () => { + // Mock that we have encountered an error the upgrade + await util.sendMockIpcResponse<AppUpgradeError>({ + channel: 'app-upgradeError', + response: 'DOWNLOAD_FAILED', + }); - const downloadProgressBarValue = await page - .getByRole('progressbar') - .getAttribute('aria-valuenow'); - expect(downloadProgressBarValue).toEqual('100'); -}); + await expect( + page.getByText( + 'Unable to download update. Check your connection and/or firewall then try again. If this problem persists, please contact support.', + ), + ).toBeVisible(); -test('App should show that it has verified the installer when verification is complete', async () => { - // Mock that we have verified the upgrade - await util.sendMockIpcResponse<AppUpgradeEvent>({ - channel: 'app-upgradeEvent', - response: { - type: 'APP_UPGRADE_STATUS_VERIFIED_INSTALLER', - }, - }); + const retryDownloadButton = selectors.retryButton(); + await expect(retryDownloadButton).toBeVisible(); - // TODO: Improve by using aria labels - await expect(page.getByText('Verification successful! Starting installer...')).toBeVisible(); - await expect(page.getByText('Download complete')).toBeVisible(); + const reportProblemButton = selectors.reportProblemButton(); + await expect(reportProblemButton).toBeVisible(); + }); + test('Should handle retrying downloading upgrade', async () => { + const retryDownloadButton = selectors.retryButton(); - const downloadProgressBarValue = await page - .getByRole('progressbar') - .getAttribute('aria-valuenow'); - expect(downloadProgressBarValue).toEqual('100'); -}); + await resolveIpcHandle( + util.mockIpcHandle({ + channel: 'appUpgrade', + response: undefined, + }), + retryDownloadButton.click(), + ); -test('App should handle failing to automatically start installer', async () => { - // Mock the verified upgrade installer path - await util.sendMockIpcResponse<IAppVersionInfo>({ - channel: 'upgradeVersion-', - response: { - supported: true, - suggestedIsBeta: false, - suggestedUpgrade: { - version: '2100.1', - changelog: [ - 'This is a changelog.', - 'Each item is on a separate line.', - 'There are three items.', - ], - verifiedInstallerPath: '/tmp/dummy-path', - }, - }, + await expect(page.getByText('Downloading...')).toBeVisible(); + await expect(page.getByText('Starting download...')).toBeVisible(); + await expectProgress(0, true); + }); }); - await util.sendMockIpcResponse<AppUpgradeError>({ - channel: 'app-upgradeError', - response: 'START_INSTALLER_AUTOMATIC_FAILED', - }); + test.describe('Should handle failing to start installer', () => { + test('Should handle failing to automatically start installer', async () => { + // Mock the verified upgrade installer path + await util.sendMockIpcResponse<IAppVersionInfo>({ + channel: 'upgradeVersion-', + response: { + supported: true, + suggestedIsBeta: false, + suggestedUpgrade: { + version: mockedVersion, + changelog: mockedChangelog, + verifiedInstallerPath: '/tmp/dummy-path', + }, + }, + }); - // TODO: Improve by using aria labels - await expect(page.getByText('Verification successful! Ready to install.')).toBeVisible(); + await util.sendMockIpcResponse<AppUpgradeError>({ + channel: 'app-upgradeError', + response: 'START_INSTALLER_AUTOMATIC_FAILED', + }); - const downloadProgressBar = page.getByRole('progressbar'); - await expect(downloadProgressBar).not.toBeVisible(); -}); + await expect(page.getByText('Verification successful! Ready to install.')).toBeVisible(); -test('App should handle failing to manually start installer', async () => { - const installUpdateButton = page.getByRole('button', { - name: 'Install update', - }); + const downloadProgressBar = selectors.downloadProgressBar(); + await expect(downloadProgressBar).not.toBeVisible(); + }); - const appUpgradePromise = util.mockIpcHandle({ - channel: 'appUpgrade', - response: undefined, - }); + test('Should handle failing to manually start installer', async () => { + const installUpdateButton = selectors.installButton(); - await installUpdateButton.click(); + await resolveIpcHandle( + util.mockIpcHandle({ + channel: 'appUpgrade', + response: undefined, + }), + installUpdateButton.click(), + ); - // The appUpgrade promise is resolved when its handle has been called. - // The handle should be called when the Install update button is clicked. - await appUpgradePromise; + // Mock that we have encountered an error the upgrade + await util.sendMockIpcResponse<AppUpgradeError>({ + channel: 'app-upgradeError', + response: 'START_INSTALLER_FAILED', + }); - // Mock that we have encountered an error the upgrade - await util.sendMockIpcResponse<AppUpgradeError>({ - channel: 'app-upgradeError', - response: 'START_INSTALLER_FAILED', - }); + await expect(installUpdateButton).not.toBeVisible(); - await expect(installUpdateButton).not.toBeVisible(); + await expect( + page.getByText( + 'Could not start the update installer, try downloading it again. If this problem persists, please contact support.', + ), + ).toBeVisible(); + const retryDownloadButton = selectors.retryButton(); + await expect(retryDownloadButton).toBeVisible(); - await expect( - page.getByText( - 'Could not start the update installer, try downloading it again. If this problem persists, please contact support.', - ), - ).toBeVisible(); - const retryDownloadButton = page.getByRole('button', { - name: 'Retry download', - }); - await expect(retryDownloadButton).toBeVisible(); + const reportProblemButton = selectors.reportProblemButton(); + await expect(reportProblemButton).toBeVisible(); + }); - const reportProblemButton = page.getByRole('button', { - name: 'Report a problem', - }); - await expect(reportProblemButton).toBeVisible(); -}); + test('Should handle retrying downloading upgrade', async () => { + const retryDownloadButton = selectors.retryButton(); -test('App should handle retrying downloading the update', async () => { - const retryDownloadButton = page.getByRole('button', { - name: 'Retry download', - }); + await resolveIpcHandle( + util.mockIpcHandle({ + channel: 'appUpgrade', + response: undefined, + }), + retryDownloadButton.click(), + ); - const appUpgradePromise = util.mockIpcHandle({ - channel: 'appUpgrade', - response: undefined, + await expect(page.getByText('Downloading...')).toBeVisible(); + await expect(page.getByText('Starting download...')).toBeVisible(); + await expectProgress(0, true); + }); }); - await retryDownloadButton.click(); + test.describe('Should cancel download', () => { + test('Should show Cancel button after upgrade started', async () => { + await startAppUpgrade(); + const cancelButton = selectors.cancelButton(); + await expect(cancelButton).toBeVisible(); + await expect(cancelButton).toBeEnabled(); + }); - // The appUpgrade promise is resolved when its handle has been called. - // The handle should be called when the Retry download button is clicked. - await appUpgradePromise; + test('Should cancel upgrade when clicking the Cancel button', async () => { + const cancelButton = selectors.cancelButton(); - // TODO: Improve by using aria labels - await expect(page.getByText('Downloading...')).toBeVisible(); - await expect(page.getByText('Starting download...')).toBeVisible(); - await expect(page.getByText('0%')).toBeVisible(); + await resolveIpcHandle( + util.mockIpcHandle({ + channel: 'appUpgradeAbort', + response: undefined, + }), + cancelButton.click(), + ); - const downloadProgressBarValue = await page - .getByRole('progressbar') - .getAttribute('aria-valuenow'); - expect(downloadProgressBarValue).toEqual('0'); -}); - -test('App should handle starting installer again if installer was started but did not close the app', async () => { - // Mock that we have started the installer. In the real app this event is sent - // after a timeout if the GUI is still running after the installer has been started - await util.sendMockIpcResponse<AppUpgradeEvent>({ - channel: 'app-upgradeEvent', - response: { - type: 'APP_UPGRADE_STATUS_STARTED_INSTALLER', - }, - }); + // After the app upgrade abort RPC is sent we expect to receive an aborted + // event. + await util.sendMockIpcResponse<AppUpgradeEvent>({ + channel: 'app-upgradeEvent', + response: { + type: 'APP_UPGRADE_STATUS_ABORTED', + }, + }); - const installUpdateButton = page.getByRole('button', { - name: 'Install update', - }); + // The cancel button should be hidden when the upgrade is aborted + await expect(cancelButton).toBeHidden(); - const appUpgradePromise = util.mockIpcHandle({ - channel: 'appUpgrade', - response: undefined, - }); + // The Download & install button should become visible again + const downloadAndInstallButton = selectors.downloadAndInstallButton(); + await expect(downloadAndInstallButton).toBeVisible(); + }); - await installUpdateButton.click(); + test('Should start upgrade again when clicking Download & install button', async () => { + await startAppUpgrade(); + const downloadAndInstallButton = selectors.downloadAndInstallButton(); - // The appUpgrade promise is resolved when its handle has been called. - // The handle should be called when the Install update button is clicked. - await appUpgradePromise; + await expect(downloadAndInstallButton).toBeHidden(); + }); + }); }); |
