diff --git a/.evergreen/buildvariants-and-tasks.in.yml b/.evergreen/buildvariants-and-tasks.in.yml index 7a6294b598c..9b0ffb82d99 100644 --- a/.evergreen/buildvariants-and-tasks.in.yml +++ b/.evergreen/buildvariants-and-tasks.in.yml @@ -482,6 +482,7 @@ tasks: - func: bootstrap vars: scope: 'compass-e2e-tests' + - func: get-compass-mongodb-com - func: smoketest-packaged-app vars: mongodb_version: latest-enterprise diff --git a/.evergreen/buildvariants-and-tasks.yml b/.evergreen/buildvariants-and-tasks.yml index 2593d9c29f5..3e3f82be0a5 100644 --- a/.evergreen/buildvariants-and-tasks.yml +++ b/.evergreen/buildvariants-and-tasks.yml @@ -498,6 +498,7 @@ tasks: - func: bootstrap vars: scope: compass-e2e-tests + - func: get-compass-mongodb-com - func: smoketest-packaged-app vars: mongodb_version: latest-enterprise diff --git a/.evergreen/functions.yml b/.evergreen/functions.yml index 3c9e71dac24..c39a52816f1 100644 --- a/.evergreen/functions.yml +++ b/.evergreen/functions.yml @@ -651,6 +651,30 @@ functions: npm run --unsafe-perm --workspace compass-e2e-tests test-packaged-ci + get-compass-mongodb-com: + - command: git.get_project + type: system + params: + directory: compass-mongodb-com + - command: shell.exec + type: setup + params: + working_dir: compass-mongodb-com + shell: bash + script: | + set -e + + echo "Using node version:"; + node -v; + echo "Using npm version:"; + npm -v; + echo "Using gcc version:" + gcc --version; + echo "Using g++ version:" + g++ --version; + + npm ci + smoketest-packaged-app: - command: shell.exec # Fail the task if it's idle for 10 mins diff --git a/.evergreen/preinstall.sh b/.evergreen/preinstall.sh index 3093b9063c0..41a3511ef2a 100755 --- a/.evergreen/preinstall.sh +++ b/.evergreen/preinstall.sh @@ -13,9 +13,9 @@ echo "APPDATA: $APPDATA" echo "PATH: $PATH" # these are super useful if you want to run the smoke tests locally -echo "DEV_VERSION_IDENTIFIER: $DEV_VERSION_IDENTIFIER" -echo "EVERGREEN_BUCKET_NAME: $EVERGREEN_BUCKET_NAME" -echo "EVERGREEN_BUCKET_KEY_PREFIX: $EVERGREEN_BUCKET_KEY_PREFIX" +echo "export DEV_VERSION_IDENTIFIER=$DEV_VERSION_IDENTIFIER" +echo "export EVERGREEN_BUCKET_KEY_PREFIX=$EVERGREEN_BUCKET_KEY_PREFIX" +echo "export EVERGREEN_BUCKET_NAME=$EVERGREEN_BUCKET_NAME" echo "IS_OSX: $IS_OSX" echo "IS_LINUX: $IS_LINUX" diff --git a/.evergreen/start-compass-mongodb-com.sh b/.evergreen/start-compass-mongodb-com.sh new file mode 100644 index 00000000000..ee8f6c08e56 --- /dev/null +++ b/.evergreen/start-compass-mongodb-com.sh @@ -0,0 +1,31 @@ +set -e + +HAS_DOCKER=false + +DOCKER_BUILD="" + +echo "Checking if docker is available ..." + +if docker version &>/dev/null; then + echo " docker is available, checking docker build" + if docker compose version &>/dev/null; then + echo " docker build is available" + HAS_DOCKER=true + DOCKER_BUILD="docker build" + else + echo " docker compose could not be found, trying standalone docker-compose as a fallback" + if docker-build version &>/dev/null; then + HAS_DOCKER=true + DOCKER_BUILD="docker-build" + else + echo " docker build not found" + fi + fi +fi + +if [ "$HAS_DOCKER" = true ]; then + $DOCKER_BUILD -t compass/web ./ + docker run -p 8080:8080 -t -e PORT=8080 -e UPDATE_CHECKER_ALLOW_DOWNGRADES=true -d compass/web +else + echo " docker not found" +fi \ No newline at end of file diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 2f4dadd4d67..516c1b992a0 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -1430,3 +1430,11 @@ export const GlobalWrites = { SampleFindingDocuments: '[data-testid="sample-finding-documents"]', SampleInsertingDocuments: '[data-testid="sample-inserting-documents"]', }; + +export const AutoUpdateToast = '[data-testid="toast-compass-update"]'; +export const AutoUpdateRestartButton = + '[data-testid="auto-update-restart-button"]'; +export const AutoUpdateDownloadLink = + '[data-testid="auto-update-download-link"]'; +export const AutoUpdateReleaseNotesLink = + '[data-testid="auto-update-release-notes-link"]'; diff --git a/packages/compass-e2e-tests/installers/helpers.ts b/packages/compass-e2e-tests/installers/helpers.ts index b1fb872f97d..db522c10bc5 100644 --- a/packages/compass-e2e-tests/installers/helpers.ts +++ b/packages/compass-e2e-tests/installers/helpers.ts @@ -8,14 +8,20 @@ export function execute( ): Promise { return new Promise((resolve, reject) => { console.log(command, ...args); + const p = spawn(command, args, { stdio: 'inherit', ...options, }); + p.on('error', (err: any) => { reject(err); }); + p.on('close', (code: number | null, signal: NodeJS.Signals | null) => { + process.off('exit', killChild); + process.off('SIGINT', interruptChild); + if (code !== null) { if (code === 0) { resolve(); @@ -41,5 +47,11 @@ export function execute( } } }); + + // Exit child process if main process exits + const killChild = () => p.kill(); + const interruptChild = () => p.kill('SIGINT'); + process.once('exit', killChild); + process.once('SIGINT', interruptChild); }); } diff --git a/packages/compass-e2e-tests/installers/types.ts b/packages/compass-e2e-tests/installers/types.ts index ec263474202..01647a56161 100644 --- a/packages/compass-e2e-tests/installers/types.ts +++ b/packages/compass-e2e-tests/installers/types.ts @@ -3,8 +3,9 @@ export type Installer = (pkg: InstallablePackage) => Promise; export type Package = { appName: string; packageFilepath: string; - // TODO: once we can download the most recent release + // TODO(COMPASS-8532): once we can download the most recent release //releaseFilepath: string; + updatable: boolean; installer: Installer; }; diff --git a/packages/compass-e2e-tests/smoke-test.ts b/packages/compass-e2e-tests/smoke-test.ts index cf6efdabdf1..1d692a2c4e8 100755 --- a/packages/compass-e2e-tests/smoke-test.ts +++ b/packages/compass-e2e-tests/smoke-test.ts @@ -8,9 +8,8 @@ import { hideBin } from 'yargs/helpers'; import https from 'https'; import { pick } from 'lodash'; import { handler as writeBuildInfo } from 'hadron-build/commands/info'; -import type { InstalledAppInfo, Package, Installer } from './installers/types'; +import type { Package, Installer } from './installers/types'; import { installMacDMG } from './installers/mac-dmg'; -import { execute } from './installers/helpers'; import { assertBuildInfoIsOSX, assertBuildInfoIsRHEL, @@ -18,6 +17,7 @@ import { assertBuildInfoIsWindows, assertCommonBuildInfo, } from './helpers/buildinfo'; +import { testAutoUpdateFrom } from './smoketests/auto-update-from'; const SUPPORTED_PLATFORMS = ['win32', 'darwin', 'linux'] as const; const SUPPORTED_ARCHS = ['x64', 'arm64'] as const; @@ -232,13 +232,15 @@ async function run() { } if (match) { - const pkg = { + const pkg: Package = { // we need appName because it is the name of the executable inside the // package, regardless of what the package filename is named or where it // gets installed appName: buildInfo.productName, packageFilepath: path.join(compassDir, 'dist', match.filename), - // TODO: releaseFilepath once we download the most recent released version too + updatable: match.updatable, + // TODO(COMPASS-8532): releaseFilepath once we download the most recent + // released version too installer: match.installer, }; @@ -252,7 +254,8 @@ async function run() { console.log(url); await downloadFile(url, pkg.packageFilepath); - // TODO: we need to also download releaseFilepath once we have that + // TODO(COMPASS-8532): we need to also download releaseFilepath once we + // have that } if (!existsSync(pkg.packageFilepath)) { @@ -261,16 +264,9 @@ async function run() { ); } - // TODO: installing either the packaged file or the released file is better - // done as part of tests so we can also clean up and install one after the - // other, but that's for a separate PR. - console.log('installing', pkg.packageFilepath); - const installedInfo = await pkg.installer({ - appName: pkg.appName, - filepath: pkg.packageFilepath, - }); - console.log('testing', installedInfo.appPath); - await testInstalledApp(pkg, installedInfo); + await testAutoUpdateFrom(pkg); + // TODO(COMPASS-8535) + //await testAutoUpdateTo(pkg); } else { throw new Error(`${context.package} not implemented`); } @@ -308,31 +304,6 @@ async function downloadFile(url: string, targetFile: string): Promise { }); } -function testInstalledApp( - pkg: Package, - appInfo: InstalledAppInfo -): Promise { - return execute( - 'npm', - [ - 'run', - '--unsafe-perm', - 'test-packaged', - '--workspace', - 'compass-e2e-tests', - '--', - '--test-filter=time-to-first-query', - ], - { - env: { - ...process.env, - COMPASS_APP_NAME: pkg.appName, - COMPASS_APP_PATH: appInfo.appPath, - }, - } - ); -} - run() .then(function () { console.log('done'); diff --git a/packages/compass-e2e-tests/smoketests/auto-update-from.ts b/packages/compass-e2e-tests/smoketests/auto-update-from.ts new file mode 100644 index 00000000000..c5ad1c715f2 --- /dev/null +++ b/packages/compass-e2e-tests/smoketests/auto-update-from.ts @@ -0,0 +1,51 @@ +import { execute } from '../installers/helpers'; +import type { Package } from '../installers/types'; + +function testInstalledApp( + pkg: Package, + appPath: string, + env: Record +): Promise { + return execute( + 'npm', + [ + 'run', + '--unsafe-perm', + 'test-packaged', + '--workspace', + 'compass-e2e-tests', + '--', + '--test-filter=auto-update', + ], + { + env: { + ...process.env, + HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE: 'http://localhost:8080', + AUTO_UPDATE_UPDATABLE: pkg.updatable.toString(), + COMPASS_APP_NAME: pkg.appName, + COMPASS_APP_PATH: appPath, + ...env, + }, + } + ); +} + +export async function testAutoUpdateFrom(pkg: Package) { + // install the app + console.log(`installing ${pkg.packageFilepath}`); + const { appPath, uninstall } = await pkg.installer({ + appName: pkg.appName, + filepath: pkg.packageFilepath, + }); + + console.log(appPath); + + try { + await testInstalledApp(pkg, appPath, { + AUTO_UPDATE_FROM: 'true', + }); + } finally { + // remove the app + await uninstall(); + } +} diff --git a/packages/compass-e2e-tests/tests/auto-update.test.ts b/packages/compass-e2e-tests/tests/auto-update.test.ts new file mode 100644 index 00000000000..ab20c67ed02 --- /dev/null +++ b/packages/compass-e2e-tests/tests/auto-update.test.ts @@ -0,0 +1,75 @@ +import { expect } from 'chai'; +import { + init, + cleanup, + Selectors, + screenshotPathName, +} from '../helpers/compass'; + +describe('Auto-update', function () { + it('auto-update from', async function () { + if (!process.env.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 + 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. + } 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' + ); + } + } finally { + await browser.screenshot(screenshotPathName('auto-update-from')); + await cleanup(compass); + } + + if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { + // 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); + } + } + }); + + it('auto-update to', function () { + if (!process.env.AUTO_UPDATE_TO) { + // 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(); + } + + // TODO + }); +});