-
Notifications
You must be signed in to change notification settings - Fork 229
feat(smoke-tests): test auto-updating from the version of Compass that was just packaged COMPASS-8536 #6629
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6b2148a
3dc10f5
6eaf996
0510a72
eec627b
57f87f0
86eca0d
1bb6e82
2d769aa
4c49473
8f0be55
c95a39c
d822498
d4b2995
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { expect } from 'chai'; | ||
import { | ||
init, | ||
cleanup, | ||
Selectors, | ||
screenshotPathName, | ||
} from '../helpers/compass'; | ||
|
||
function wait(ms: number) { | ||
return new Promise((resolve) => { | ||
setTimeout(resolve, ms); | ||
}); | ||
} | ||
|
||
describe('Auto-update', function () { | ||
it('auto-update from', async function () { | ||
if (process.env.TEST_NAME !== 'auto-update-from') { | ||
// we don't want this test to execute along with all the others under | ||
// normal circumstances because it is destructive - it overwrites Compass | ||
// itself | ||
this.skip(); | ||
} | ||
|
||
// run the app and wait for it to auto-update | ||
console.log('starting compass the first time'); | ||
const compass = await init('auto-update from', { firstRun: true }); | ||
const { browser } = compass; | ||
try { | ||
await browser.$(Selectors.AutoUpdateToast).waitForDisplayed(); | ||
|
||
if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { | ||
const restartButton = browser.$(Selectors.AutoUpdateRestartButton); | ||
await restartButton.waitForDisplayed(); | ||
|
||
// We could click the restart button to apply the update and restart the | ||
// app, but restarting the app confuses webdriverio or at least our test | ||
// helpers. So we're going to just restart the app manually. | ||
await browser.pause(1000); | ||
} else { | ||
// When auto-update is not supported the toast contains a link to | ||
// download | ||
const linkElement = browser.$(Selectors.AutoUpdateDownloadLink); | ||
await linkElement.waitForDisplayed(); | ||
expect(await linkElement.getAttribute('href')).to.equal( | ||
'https://www.mongodb.com/try/download/compass?utm_source=compass&utm_medium=product' | ||
); | ||
} | ||
} finally { | ||
await browser.screenshot(screenshotPathName('auto-update-from')); | ||
await cleanup(compass); | ||
} | ||
|
||
if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { | ||
console.log( | ||
'pause to make sure the app properly exited before starting again' | ||
); | ||
await wait(10_000); | ||
|
||
console.log('starting compass a second time'); | ||
// run the app again and check that the version changed | ||
const compass = await init('auto-update from restart', { | ||
firstRun: false, | ||
}); | ||
const { browser } = compass; | ||
try { | ||
await browser.$(Selectors.AutoUpdateToast).waitForDisplayed(); | ||
await browser | ||
.$(Selectors.AutoUpdateReleaseNotesLink) | ||
.waitForDisplayed(); | ||
} finally { | ||
await browser.screenshot( | ||
screenshotPathName('auto-update-from-restart') | ||
); | ||
await cleanup(compass); | ||
} | ||
} | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
ignores: | ||
- '@mongodb-js/prettier-config-compass' | ||
- '@mongodb-js/tsconfig-compass' | ||
- compass-mongodb-com |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,11 +2,12 @@ | |
import assert from 'node:assert/strict'; | ||
import fs from 'node:fs'; | ||
import path from 'node:path'; | ||
import { once } from 'node:events'; | ||
|
||
import yargs from 'yargs'; | ||
import { hideBin } from 'yargs/helpers'; | ||
import { pick } from 'lodash'; | ||
import { execute } from './execute'; | ||
import { execute, executeAsync } from './execute'; | ||
import { | ||
type PackageDetails, | ||
readPackageDetails, | ||
|
@@ -25,6 +26,8 @@ import { installWindowsMSI } from './installers/windows-msi'; | |
const SUPPORTED_PLATFORMS = ['win32', 'darwin', 'linux'] as const; | ||
const SUPPORTED_ARCHS = ['x64', 'arm64'] as const; | ||
|
||
const SUPPORTED_TESTS = ['time-to-first-query', 'auto-update-from'] as const; | ||
|
||
function isSupportedPlatform( | ||
value: unknown | ||
): value is typeof SUPPORTED_PLATFORMS[number] { | ||
|
@@ -104,7 +107,14 @@ const argv = yargs(hideBin(process.argv)) | |
type: 'boolean', | ||
description: 'Do not delete the sandbox after a run', | ||
default: false, | ||
}); | ||
}) | ||
.option('tests', { | ||
type: 'array', | ||
string: true, | ||
choices: SUPPORTED_TESTS, | ||
description: 'Which tests to run', | ||
}) | ||
.default('tests', SUPPORTED_TESTS.slice()); | ||
|
||
type TestSubject = PackageDetails & { | ||
filepath: string; | ||
|
@@ -184,23 +194,47 @@ async function run() { | |
'platform', | ||
'arch', | ||
'package', | ||
'tests', | ||
]) | ||
); | ||
|
||
const { kind, filepath, appName } = await getTestSubject(context); | ||
const { kind, appName, filepath, autoUpdatable } = await getTestSubject( | ||
context | ||
); | ||
const install = getInstaller(kind); | ||
|
||
try { | ||
const { appPath, uninstall } = install({ | ||
appName, | ||
filepath, | ||
destinationPath: context.sandboxPath, | ||
}); | ||
if (context.tests.length === 0) { | ||
console.log('Warning: not performing any tests. Pass --tests.'); | ||
lerouxb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
try { | ||
runTest({ appName, appPath }); | ||
} finally { | ||
await uninstall(); | ||
for (const testName of context.tests) { | ||
const { appPath, uninstall } = install({ | ||
appName, | ||
filepath, | ||
destinationPath: context.sandboxPath, | ||
}); | ||
|
||
try { | ||
if (testName === 'time-to-first-query') { | ||
// Auto-update does not work on mac in CI at the moment. So in that case | ||
// we just run the E2E tests to make sure the app at least starts up. | ||
runTimeToFirstQuery({ | ||
appName, | ||
appPath, | ||
}); | ||
} | ||
if (testName === 'auto-update-from') { | ||
await runUpdateTest({ | ||
appName, | ||
appPath, | ||
autoUpdatable, | ||
testName, | ||
}); | ||
} | ||
} finally { | ||
await uninstall(); | ||
} | ||
} | ||
} finally { | ||
if (context.skipCleanup) { | ||
|
@@ -212,12 +246,30 @@ async function run() { | |
} | ||
} | ||
|
||
type RunTestOptions = { | ||
async function importUpdateServer() { | ||
try { | ||
return (await import('compass-mongodb-com')).default; | ||
} catch (err: unknown) { | ||
console.log('Remember to npm link compass-mongodb-com'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you really want to ... you could throw a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm good :) |
||
throw err; | ||
} | ||
} | ||
|
||
async function startAutoUpdateServer() { | ||
console.log('Starting auto-update server'); | ||
const { httpServer, updateChecker, start } = (await importUpdateServer())(); | ||
start(); | ||
await once(updateChecker, 'refreshed'); | ||
|
||
return httpServer; | ||
} | ||
|
||
type RunE2ETestOptions = { | ||
appName: string; | ||
appPath: string; | ||
}; | ||
|
||
function runTest({ appName, appPath }: RunTestOptions) { | ||
function runTimeToFirstQuery({ appName, appPath }: RunE2ETestOptions) { | ||
execute( | ||
'npm', | ||
[ | ||
|
@@ -241,6 +293,63 @@ function runTest({ appName, appPath }: RunTestOptions) { | |
); | ||
} | ||
|
||
type RunUpdateTestOptions = { | ||
appName: string; | ||
appPath: string; | ||
autoUpdatable?: boolean; | ||
testName: string; | ||
}; | ||
|
||
async function runUpdateTest({ | ||
appName, | ||
appPath, | ||
autoUpdatable, | ||
testName, | ||
}: RunUpdateTestOptions) { | ||
process.env.PORT = '0'; // dynamic port | ||
process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES = 'true'; | ||
|
||
const server = await startAutoUpdateServer(); | ||
|
||
const address = server.address(); | ||
assert(typeof address === 'object' && address !== null); | ||
const port = address.port; | ||
const HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE = `http://localhost:${port}`; | ||
console.log({ HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE }); | ||
|
||
try { | ||
// must be async because the update server is running in the same process | ||
await executeAsync( | ||
'npm', | ||
[ | ||
'run', | ||
'--unsafe-perm', | ||
'test-packaged', | ||
'--workspace', | ||
'compass-e2e-tests', | ||
'--', | ||
'--test-filter=auto-update', | ||
], | ||
{ | ||
// We need to use a shell to get environment variables setup correctly | ||
shell: true, | ||
env: { | ||
...process.env, | ||
HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, | ||
AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), | ||
TEST_NAME: testName, | ||
COMPASS_APP_NAME: appName, | ||
COMPASS_APP_PATH: appPath, | ||
}, | ||
} | ||
); | ||
} finally { | ||
console.log('Stopping auto-update server'); | ||
server.close(); | ||
delete process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES; | ||
} | ||
} | ||
|
||
run() | ||
.then(function () { | ||
console.log('done'); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
declare module 'compass-mongodb-com' { | ||
import type http from 'http'; | ||
function updateServer(): { | ||
start: () => void; | ||
httpServer: http.Server; | ||
updateChecker: NodeJS.EventEmitter; | ||
}; | ||
export = updateServer; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have another branch open where I'm switching from console.log to debug. I don't want to conflict with that now, so will fix all these together in a future PR.