diff --git a/packages/compass-connections-navigation/src/navigation-item.tsx b/packages/compass-connections-navigation/src/navigation-item.tsx index cb4f5899ca5..e10469f84e8 100644 --- a/packages/compass-connections-navigation/src/navigation-item.tsx +++ b/packages/compass-connections-navigation/src/navigation-item.tsx @@ -184,20 +184,21 @@ export function NavigationItem({ } if (item.type === 'connection') { return { - 'data-is-active': `${isActive}`, + 'data-is-active': isActive.toString(), 'data-connection-id': item.connectionInfo.id, 'data-connection-name': item.name, + 'data-is-connected': (item.connectionStatus === 'connected').toString(), }; } if (item.type === 'database') { return { - 'data-is-active': `${isActive}`, + 'data-is-active': isActive.toString(), 'data-connection-id': item.connectionId, 'data-database-name': item.dbName, }; } return { - 'data-is-active': `${isActive}`, + 'data-is-active': isActive.toString(), 'data-connection-id': item.connectionId, 'data-namespace': item.namespace, }; diff --git a/packages/compass-e2e-tests/helpers/commands/add-collection.ts b/packages/compass-e2e-tests/helpers/commands/add-collection.ts index 98eeb194344..36cb786b372 100644 --- a/packages/compass-e2e-tests/helpers/commands/add-collection.ts +++ b/packages/compass-e2e-tests/helpers/commands/add-collection.ts @@ -137,8 +137,6 @@ export async function addCollection( timeoutMsg: `Failed to select a value "${valStr}" for "${key}" in Select after ${clickAttempt} attempt(s)`, } ); - - await browser.screenshot(`custom-collation-${key}-${valStr}.png`); } // scroll to the locale one so the screenshot will include it. diff --git a/packages/compass-e2e-tests/helpers/commands/close-settings-modal.ts b/packages/compass-e2e-tests/helpers/commands/close-settings-modal.ts index b19419a5330..951f2c8ca7c 100644 --- a/packages/compass-e2e-tests/helpers/commands/close-settings-modal.ts +++ b/packages/compass-e2e-tests/helpers/commands/close-settings-modal.ts @@ -11,7 +11,6 @@ export async function closeSettingsModal( const settingsModalElement = await browser.$(Selectors.SettingsModal); await settingsModalElement.waitForDisplayed(); - await browser.screenshot('settings-modal.png'); await browser.clickVisible(Selectors.CloseSettingsModalButton); await settingsModalElement.waitForDisplayed({ diff --git a/packages/compass-e2e-tests/helpers/commands/close-welcome-modal.ts b/packages/compass-e2e-tests/helpers/commands/close-welcome-modal.ts index 2b16929351e..99f41867077 100644 --- a/packages/compass-e2e-tests/helpers/commands/close-welcome-modal.ts +++ b/packages/compass-e2e-tests/helpers/commands/close-welcome-modal.ts @@ -11,8 +11,6 @@ export async function closeWelcomeModal( const welcomeModalElement = await browser.$(Selectors.WelcomeModal); await welcomeModalElement.waitForDisplayed(); - await browser.screenshot('welcome-modal.png'); - await browser.clickVisible(Selectors.CloseWelcomeModalButton); await welcomeModalElement.waitForDisplayed({ reverse: true, diff --git a/packages/compass-e2e-tests/helpers/commands/connect-form.ts b/packages/compass-e2e-tests/helpers/commands/connect-form.ts new file mode 100644 index 00000000000..12ea2fcdf32 --- /dev/null +++ b/packages/compass-e2e-tests/helpers/commands/connect-form.ts @@ -0,0 +1,1013 @@ +import _ from 'lodash'; +import { expect } from 'chai'; +import type { CompassBrowser } from '../compass-browser'; +import * as Selectors from '../selectors'; +import type { ConnectFormState } from '../connect-form-state'; +import { + DEFAULT_CONNECTION_NAME_1, + DEFAULT_CONNECTION_NAME_2, + DEFAULT_CONNECTION_STRING_1, + DEFAULT_CONNECTION_STRING_2, + TEST_COMPASS_WEB, + TEST_MULTIPLE_CONNECTIONS, +} from '../compass'; +import Debug from 'debug'; +const debug = Debug('compass-e2e-tests'); + +export async function resetConnectForm(browser: CompassBrowser): Promise { + const Sidebar = TEST_MULTIPLE_CONNECTIONS + ? Selectors.Multiple + : Selectors.Single; + + if (TEST_MULTIPLE_CONNECTIONS) { + if (await browser.$(Selectors.ConnectionModal).isDisplayed()) { + await browser.clickVisible(Selectors.ConnectionModalCloseButton); + await browser + .$(Selectors.ConnectionModal) + .waitForDisplayed({ reverse: true }); + } + } + + await browser.clickVisible(Sidebar.SidebarNewConnectionButton); + + const connectionTitleSelector = TEST_MULTIPLE_CONNECTIONS + ? Selectors.ConnectionModalTitle + : Selectors.ConnectionTitle; + + const connectionTitle = await browser.$(connectionTitleSelector); + await connectionTitle.waitUntil(async () => { + return (await connectionTitle.getText()) === 'New Connection'; + }); + + await browser.waitUntil(async () => { + return ( + (await browser.getConnectFormConnectionString(true)) === + 'mongodb://localhost:27017' + ); + }); +} + +export async function getConnectFormState( + browser: CompassBrowser, + isFocused = false +): Promise { + const wasExpanded = await browser.expandAccordion( + Selectors.ConnectionFormAdvancedToggle + ); + + const connectionString = await browser.getConnectFormConnectionString( + isFocused + ); + + // General + const initialTab = await browser.navigateToConnectTab('General'); + + const defaultPromises: Record> = { + scheme: getCheckedRadioValue(browser, Selectors.ConnectionFormSchemeRadios), + hosts: getMultipleValues(browser, Selectors.ConnectionFormHostInputs), + directConnection: getCheckboxValue( + browser, + Selectors.ConnectionFormDirectConnectionCheckbox + ), + }; + if (TEST_MULTIPLE_CONNECTIONS) { + defaultPromises.connectionName = getValue( + browser, + Selectors.ConnectionFormConnectionName + ); + defaultPromises.connectionColor = getValue( + browser, + Selectors.ConnectionFormConnectionColor + ); + defaultPromises.connectionFavorite = getCheckboxValue( + browser, + Selectors.ConnectionFormFavoriteCheckbox + ); + } + const defaultState = await promiseMap(defaultPromises); + + // Authentication + await browser.navigateToConnectTab('Authentication'); + const authenticationState = await promiseMap({ + authMethod: getCheckedRadioValue( + browser, + Selectors.ConnectionFormAuthenticationMethodRadios + ), + + // Username/Password + defaultUsername: getValue(browser, Selectors.ConnectionFormInputUsername), + defaultPassword: getValue(browser, Selectors.ConnectionFormInputPassword), + defaultAuthSource: getValue( + browser, + Selectors.ConnectionFormInputAuthSource + ), + defaultAuthMechanism: getCheckedRadioValue( + browser, + Selectors.ConnectionFormAuthMechanismRadios + ), + + // OIDC + oidcUsername: getValue(browser, Selectors.ConnectionFormInputOIDCUsername), + + // Kerberos + kerberosPrincipal: getValue( + browser, + Selectors.ConnectionFormInputGssApiPrincipal + ), + kerberosServiceName: getValue( + browser, + Selectors.ConnectionFormInputGssApiServiceName + ), + kerberosCanonicalizeHostname: getCheckedRadioValue( + browser, + Selectors.ConnectionFormCanonicalizeHostNameRadios + ), + kerberosServiceRealm: getValue( + browser, + Selectors.ConnectionFormInputGssApiServiceRealm + ), + kerberosProvidePassword: getCheckboxValue( + browser, + Selectors.ConnectionFormGssApiPasswordCheckbox + ), + kerberosPassword: getValue( + browser, + Selectors.ConnectionFormInputGssApiPassword + ), + // LDAP + ldapUsername: getValue(browser, Selectors.ConnectionFormInputPlainUsername), + ldapPassword: getValue(browser, Selectors.ConnectionFormInputPlainPassword), + + // AWS IAM + awsAccessKeyId: getValue( + browser, + Selectors.ConnectionFormInputAWSAccessKeyId + ), + awsSecretAccessKey: getValue( + browser, + Selectors.ConnectionFormInputAWSSecretAccessKey + ), + awsSessionToken: getValue( + browser, + Selectors.ConnectionFormInputAWSSessionToken + ), + }); + + // TLS/SSL + await browser.navigateToConnectTab('TLS/SSL'); + const tlsState = await promiseMap({ + sslConnection: getCheckedRadioValue( + browser, + Selectors.ConnectionFormSSLConnectionRadios + ), + + // these are just the button text, not actually the file path + tlsCAFile: getFilename(browser, Selectors.ConnectionFormTlsCaButton), + tlsCertificateKeyFile: getFilename( + browser, + Selectors.ConnectionFormTlsCertificateKeyButton + ), + + clientKeyPassword: getValue( + browser, + Selectors.ConnectionFormInputTlsCertificateKeyFilePassword + ), + tlsInsecure: getCheckboxValue( + browser, + Selectors.ConnectionFormTlsInsecureCheckbox + ), + tlsAllowInvalidHostnames: getCheckboxValue( + browser, + Selectors.ConnectionFormTlsAllowInvalidHostnamesCheckbox + ), + tlsAllowInvalidCertificates: getCheckboxValue( + browser, + Selectors.ConnectionFormTlsAllowInvalidCertificatesCheckbox + ), + useSystemCA: getCheckboxValue( + browser, + Selectors.ConnectionFormTlsUseSystemCACheckbox + ), + }); + + // Proxy/SSH + await browser.navigateToConnectTab('Proxy/SSH'); + + const proxyState = await promiseMap({ + proxyMethod: getCheckedRadioValue( + browser, + Selectors.ConnectionFormProxyMethodRadios + ), + + // SSH with Password + // NOTE: these don't go in the URI so will likely never return values + sshPasswordHost: getValue( + browser, + Selectors.ConnectionFormInputSshPasswordHost + ), + sshPasswordPort: getValue( + browser, + Selectors.ConnectionFormInputSshPasswordPort + ), + sshPasswordUsername: getValue( + browser, + Selectors.ConnectionFormInputSshPasswordUsername + ), + sshPasswordPassword: getValue( + browser, + Selectors.ConnectionFormInputSshPasswordPassword + ), + + // SSH with Identity File + // NOTE: same as above these are unlikely ever return values in these tests + sshIdentityHost: getValue( + browser, + Selectors.ConnectionFormInputSshIdentityHost + ), + sshIdentityPort: getValue( + browser, + Selectors.ConnectionFormInputSshIdentityPort + ), + sshIdentityUsername: getValue( + browser, + Selectors.ConnectionFormInputSshIdentityUsername + ), + // same as above: this is the button text, not the file path + sshIdentityKeyFile: getFilename( + browser, + Selectors.ConnectionFormSshIdentityKeyButton + ), + sshIdentityPassword: getValue( + browser, + Selectors.ConnectionFormInputSshIdentityPassword + ), + + socksHost: getValue(browser, Selectors.ConnectionFormInputSocksHost), + socksPort: getValue(browser, Selectors.ConnectionFormInputSocksPort), + socksUsername: getValue( + browser, + Selectors.ConnectionFormInputSocksUsername + ), + socksPassword: getValue( + browser, + Selectors.ConnectionFormInputSocksPassword + ), + }); + + // FLE2 + await browser.navigateToConnectTab('In-Use Encryption'); + await browser.expandAccordion(Selectors.ConnectionFormInputFLELocalKMS); + + const inUseEncryptionState = await promiseMap({ + fleKeyVaultNamespace: getValue( + browser, + Selectors.ConnectionFormInputFLEKeyVaultNamespace + ), + fleStoreCredentials: getCheckboxValue( + browser, + Selectors.ConnectionFormInputFLEStoreCredentialsCheckbox + ), + fleKey: getValue(browser, Selectors.ConnectionFormInputFLELocalKMS), + fleEncryptedFieldsMap: browser.getCodemirrorEditorText( + Selectors.ConnectionFormInputFLEEncryptedFieldsMapEditor + ), + }); + + // Advanced + await browser.navigateToConnectTab('Advanced'); + const advancedState = await promiseMap({ + readPreference: getCheckedRadioValue( + browser, + Selectors.ConnectionFormReadPreferenceRadios + ), + replicaSet: getValue(browser, Selectors.ConnectionFormInputReplicaset), + defaultDatabase: getValue( + browser, + Selectors.ConnectionFormInputDefaultDatabase + ), + urlOptionKeys: getMultipleValues( + browser, + Selectors.ConnectionFormUrlOptionKeys + ), + urlOptionValues: getMultipleValues( + browser, + Selectors.ConnectionFormUrlOptionValues + ), + }); + + if (advancedState.urlOptionKeys) { + advancedState.urlOptions = Object.fromEntries( + advancedState.urlOptionKeys.map((k: string, i: number) => [ + k, + advancedState.urlOptionValues[i], + ]) + ); + + delete advancedState.urlOptionKeys; + delete advancedState.urlOptionValues; + } + + const result = { + connectionString, + ...defaultState, + ...authenticationState, + ...tlsState, + ...proxyState, + ...advancedState, + ...inUseEncryptionState, + }; + + // restore the initial state + if (wasExpanded) { + // get back to the tab it was on + await browser.navigateToConnectTab(initialTab); + } else { + // collapse it again + await browser.clickVisible(Selectors.ConnectionFormAdvancedToggle); + + await browser.waitUntil(async () => { + const advancedButton = await browser.$( + Selectors.ConnectionFormAdvancedToggle + ); + return (await advancedButton.getAttribute('aria-expanded')) === 'false'; + }); + } + + return result; +} + +async function getCheckedRadioValue( + browser: CompassBrowser, + selector: string +): Promise { + const elements = await browser.$$(selector); + for (const element of elements) { + if (await element.isSelected()) { + return element.getValue(); + } + } + + return null; +} + +async function getCheckboxValue( + browser: CompassBrowser, + selector: string +): Promise { + const element = await browser.$(selector); + if (!(await element.isExisting())) { + return null; // as opposed to true for checked and false for not + } + + return element.isSelected(); +} + +async function getText( + browser: CompassBrowser, + selector: string +): Promise { + const element = await browser.$(selector); + if (!(await element.isExisting())) { + return null; + } + + const text = await element.getText(); + return text || null; +} + +async function getFilename( + browser: CompassBrowser, + selector: string +): Promise { + const text = await getText(browser, selector); + return text === 'Select a file...' ? null : text; +} + +async function getValue( + browser: CompassBrowser, + selector: string +): Promise { + const element = await browser.$(selector); + if (!(await element.isExisting())) { + return null; + } + + const value = await element.getValue(); + return value || null; +} + +async function getMultipleValues( + browser: CompassBrowser, + selector: string +): Promise { + const results = ( + await browser.$$(selector).map((element) => element.getValue()) + ).filter((result) => result !== ''); + + return results.length ? results : null; +} + +interface NamedPromises { + [key: string]: Promise; +} + +async function promiseMap(map: NamedPromises) { + const results = await Promise.all(Object.values(map)); + return Object.fromEntries( + Object.keys(map) + .map((k, i) => [k, results[i]]) + .filter(([, v]) => v !== null) + ); +} + +async function waitForElementAnimations(browser: CompassBrowser, element: any) { + let previousResult = { + ...(await element.getLocation()), + ...(await element.getSize()), + }; + await browser.waitUntil(async function () { + // small delay to make sure that if it is busy animating it had time to move + // before the first check and between each two checks + await browser.pause(50); + + const result = { + ...(await element.getLocation()), + ...(await element.getSize()), + }; + const stopped = _.isEqual(result, previousResult); + previousResult = result; + return stopped; + }); +} + +const colorMap: Record = { + 'no-color': 'No Color', + color1: 'Red', + color2: 'Pink', + color3: 'Orange', + color4: 'Yellow', + color5: 'Green', + color6: 'Teal', + color7: 'Blue', + color8: 'Iris', + color9: 'Purple', +}; + +function colorValueToName(color: string): string { + if (colorMap[color]) { + return colorMap[color]; + } + return color; +} + +export async function setConnectFormState( + browser: CompassBrowser, + state: ConnectFormState +): Promise { + await browser.resetConnectForm(); + + // Something to keep in mind is that if you specify both connectionString AND + // other options, then the other options are going to override the + // connectionString. You probably want just one or the other. + if (state.connectionString) { + await browser.setValueVisible( + Selectors.ConnectionFormStringInput, + state.connectionString + ); + } + + await browser.expandAccordion(Selectors.ConnectionFormAdvancedToggle); + + // General + await browser.navigateToConnectTab('General'); + + if (state.scheme) { + await browser.clickParent( + Selectors.connectionFormSchemeRadio(state.scheme) + ); + } + if (state.hosts) { + for (let i = 0; i < state.hosts.length; ++i) { + if (i > 0) { + await browser.clickVisible( + '[data-testid="connection-add-host-button"]:last-child' + ); + } + await browser.setValueVisible( + `#connection-host-input-${i}`, + state.hosts[i] + ); + } + } + if (state.directConnection) { + await browser.clickParent(Selectors.ConnectionFormDirectConnectionCheckbox); + } + + if (TEST_MULTIPLE_CONNECTIONS) { + // Name, Color, Favorite + if (state.connectionName) { + await browser.setValueVisible( + Selectors.ConnectionFormConnectionName, + state.connectionName + ); + } + + if (state.connectionColor) { + await browser.selectOption( + Selectors.ConnectionFormConnectionColor, + colorValueToName(state.connectionColor) + ); + } + + if (state.connectionFavorite) { + await browser.clickParent(Selectors.ConnectionFormFavoriteCheckbox); + } + } + + // Authentication + if ( + state.authMethod || + state.defaultUsername || + state.defaultAuthSource || + state.defaultAuthMechanism || + state.kerberosPrincipal || + state.kerberosPrincipal || + state.kerberosServiceName || + state.kerberosCanonicalizeHostname || + state.kerberosServiceRealm || + state.kerberosProvidePassword || + state.kerberosPassword + ) { + await browser.navigateToConnectTab('Authentication'); + + if (state.authMethod) { + await browser.clickParent( + Selectors.connectionFormAuthenticationMethodRadio(state.authMethod) + ); + } + + // Username/Password + if (state.defaultUsername && state.defaultPassword) { + await browser.setValueVisible( + Selectors.ConnectionFormInputUsername, + state.defaultUsername + ); + await browser.setValueVisible( + Selectors.ConnectionFormInputPassword, + state.defaultPassword + ); + } + if (state.defaultAuthSource) { + await browser.setValueVisible( + Selectors.ConnectionFormInputAuthSource, + state.defaultAuthSource + ); + } + if (state.defaultAuthMechanism) { + await browser.clickParent( + Selectors.connectionFormAuthMechanismRadio(state.defaultAuthMechanism) + ); + } + + // Kerberos + if (state.kerberosPrincipal) { + await browser.setValueVisible( + Selectors.ConnectionFormInputGssApiPrincipal, + state.kerberosPrincipal + ); + } + if (state.kerberosServiceName) { + await browser.setValueVisible( + Selectors.ConnectionFormInputGssApiServiceName, + state.kerberosServiceName + ); + } + if (state.kerberosCanonicalizeHostname) { + await browser.clickParent( + Selectors.connectionFormCanonicalizeHostNameRadio( + state.kerberosCanonicalizeHostname + ) + ); + } + if (state.kerberosServiceRealm) { + await browser.setValueVisible( + Selectors.ConnectionFormInputGssApiServiceRealm, + state.kerberosServiceRealm + ); + } + if (state.kerberosProvidePassword) { + await browser.clickParent(Selectors.ConnectionFormGssApiPasswordCheckbox); + } + if (state.kerberosPassword) { + await browser.setValueVisible( + Selectors.ConnectionFormInputGssApiPassword, + state.kerberosPassword + ); + } + + // LDAP + if (state.ldapUsername && state.ldapPassword) { + await browser.setValueVisible( + Selectors.ConnectionFormInputPlainUsername, + state.ldapUsername + ); + await browser.setValueVisible( + Selectors.ConnectionFormInputPlainPassword, + state.ldapPassword + ); + } + + // AWS IAM + if (state.awsAccessKeyId && state.awsSecretAccessKey) { + await browser.setValueVisible( + Selectors.ConnectionFormInputAWSAccessKeyId, + state.awsAccessKeyId + ); + await browser.setValueVisible( + Selectors.ConnectionFormInputAWSSecretAccessKey, + state.awsSecretAccessKey + ); + } + if (state.awsSessionToken) { + await browser.setValueVisible( + Selectors.ConnectionFormInputAWSSessionToken, + state.awsSessionToken + ); + } + if (state.awsSessionToken) { + await browser.setValueVisible( + Selectors.ConnectionFormInputAWSSessionToken, + state.awsSessionToken + ); + } + + // OIDC + if (state.oidcUsername) { + await browser.setValueVisible( + Selectors.ConnectionFormInputOIDCUsername, + state.oidcUsername + ); + } + } + + // FLE2 + if ( + state.fleKeyVaultNamespace || + state.fleKey || + state.fleEncryptedFieldsMap + ) { + await browser.navigateToConnectTab('In-Use Encryption'); + + if (state.fleKeyVaultNamespace) { + await browser.setValueVisible( + Selectors.ConnectionFormInputFLEKeyVaultNamespace, + state.fleKeyVaultNamespace + ); + } + if (state.fleKey) { + await browser.expandAccordion(Selectors.ConnectionFormInputFLELocalKMS); + await browser.setValueVisible( + Selectors.ConnectionFormInputFLELocalKey, + state.fleKey + ); + } + if (state.fleEncryptedFieldsMap) { + // set the text in the editor + await browser.setCodemirrorEditorValue( + Selectors.ConnectionFormInputFLEEncryptedFieldsMapEditor, + state.fleEncryptedFieldsMap + ); + } + } + + // TLS/SSL + if ( + state.sslConnection || + state.tlsCAFile || + state.tlsCertificateKeyFile || + state.clientKeyPassword || + state.tlsInsecure || + state.tlsAllowInvalidHostnames || + state.tlsAllowInvalidCertificates || + state.useSystemCA + ) { + await browser.navigateToConnectTab('TLS/SSL'); + + if (state.sslConnection) { + await browser.clickParent( + Selectors.connectionFormSSLConnectionRadio(state.sslConnection) + ); + } + if (state.tlsCAFile) { + await browser.selectFile( + Selectors.ConnectionFormTlsCaFile, + state.tlsCAFile + ); + } + if (state.tlsCertificateKeyFile) { + await browser.selectFile( + Selectors.ConnectionFormTlsCertificateKeyFile, + state.tlsCertificateKeyFile + ); + } + if (state.clientKeyPassword) { + await browser.setValueVisible( + Selectors.ConnectionFormInputTlsCertificateKeyFilePassword, + state.clientKeyPassword + ); + } + if (state.tlsInsecure) { + await browser.clickParent(Selectors.ConnectionFormTlsInsecureCheckbox); + } + if (state.tlsAllowInvalidHostnames) { + await browser.clickParent( + Selectors.ConnectionFormTlsAllowInvalidHostnamesCheckbox + ); + } + if (state.tlsAllowInvalidCertificates) { + await browser.clickParent( + Selectors.ConnectionFormTlsAllowInvalidCertificatesCheckbox + ); + } + if (state.useSystemCA) { + await browser.clickParent(Selectors.ConnectionFormTlsUseSystemCACheckbox); + } + } + + // Proxy/SSH + if ( + state.proxyMethod || + state.sshPasswordHost || + state.sshPasswordUsername || + state.sshPasswordPassword || + state.sshIdentityHost || + state.sshIdentityUsername || + state.sshIdentityKeyFile || + state.sshIdentityPassword || + state.socksHost || + state.socksPort || + state.socksUsername || + state.socksPassword + ) { + await browser.navigateToConnectTab('Proxy/SSH'); + + //proxyMethod + if (state.proxyMethod) { + await browser.clickParent( + Selectors.connectionFormProxyMethodRadio(state.proxyMethod) + ); + } + + // SSH with Password + // NOTE: these don't affect the URI + if (state.sshPasswordHost && state.sshPasswordPort) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSshPasswordHost, + state.sshPasswordHost + ); + await browser.setValueVisible( + Selectors.ConnectionFormInputSshPasswordPort, + state.sshPasswordPort + ); + } + if (state.sshPasswordUsername) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSshPasswordUsername, + state.sshPasswordUsername + ); + } + if (state.sshPasswordPassword) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSshPasswordPassword, + state.sshPasswordPassword + ); + } + + // SSH with Identity File + // NOTE: these don't affect the URI + if (state.sshIdentityHost && state.sshIdentityPort) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSshIdentityHost, + state.sshIdentityHost + ); + await browser.setValueVisible( + Selectors.ConnectionFormInputSshIdentityPort, + state.sshIdentityPort + ); + } + if (state.sshIdentityUsername) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSshIdentityUsername, + state.sshIdentityUsername + ); + } + if (state.sshIdentityKeyFile) { + await browser.selectFile( + Selectors.ConnectionFormSshIdentityKeyFile, + state.sshIdentityKeyFile + ); + } + if (state.sshIdentityPassword) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSshIdentityPassword, + state.sshIdentityPassword + ); + } + + // Socks5 + if (state.socksHost) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSocksHost, + state.socksHost + ); + } + if (state.socksPort) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSocksPort, + state.socksPort + ); + } + if (state.socksUsername) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSocksUsername, + state.socksUsername + ); + } + if (state.socksPassword) { + await browser.setValueVisible( + Selectors.ConnectionFormInputSocksPassword, + state.socksPassword + ); + } + } + + // Advanced + if ( + state.readPreference || + state.replicaSet || + state.defaultDatabase || + state.urlOptions + ) { + await browser.navigateToConnectTab('Advanced'); + + if (state.readPreference) { + await browser.clickParent( + Selectors.connectionFormReadPreferenceRadio(state.readPreference) + ); + } + if (state.replicaSet) { + await browser.setValueVisible( + Selectors.ConnectionFormInputReplicaset, + state.replicaSet + ); + } + if (state.defaultDatabase) { + await browser.setValueVisible( + Selectors.ConnectionFormInputDefaultDatabase, + state.defaultDatabase + ); + } + if (state.urlOptions) { + for (const [index, [key, value]] of Object.entries( + state.urlOptions + ).entries()) { + // key + await browser.clickVisible( + Selectors.connectionFormUrlOptionKeyButton(index) + ); + + let found = false; + let allText: string[] = []; + + // for whatever reasons sometimes the first one or two come through as empty strings + await browser.waitUntil(async () => { + allText = []; + const options = await browser.$$('#select-key-menu [role="option"]'); + for (const option of options) { + const _text = await option.getText(); + const text = _text.trim(); + allText.push(text); + if (text === key) { + found = true; + await option.scrollIntoView(); + await option.waitForDisplayed(); + await waitForElementAnimations(browser, option); + await option.click(); + break; + } + } + return found; + }); + + // make sure we found and clicked on an option + expect( + found, + `Could not find URL option "${key}". Found "${allText.join(', ')}"` + ).to.be.true; + + // make sure the menu goes away once we clicked on the option + const menu = await browser.$('#select-key-menu'); + await menu.waitForExist({ reverse: true }); + + // value + await browser.setValueVisible( + Selectors.connectionFormUrlOptionValueInput(index), + value + ); + } + } + } +} + +export async function saveConnection( + browser: CompassBrowser, + state: ConnectFormState, + + // TODO(COMPASS-8023): Just remove these once the single connection code is removed + favouriteName: string, + color: string +): Promise { + await browser.setConnectFormState(state); + if (TEST_MULTIPLE_CONNECTIONS) { + await browser.clickVisible(Selectors.ConnectionModalSaveButton); + await browser + .$(Selectors.ConnectionModal) + .waitForDisplayed({ reverse: true }); + } else { + await browser.clickVisible(Selectors.ConnectionEditFavouriteButton); + await browser.$(Selectors.FavoriteModal).waitForDisplayed(); + await browser.setValueVisible(Selectors.FavoriteNameInput, favouriteName); + await browser.clickVisible( + `${Selectors.FavoriteColorSelector} [data-testid="color-pick-${color}"]` + ); + await browser.$(Selectors.FavoriteSaveButton).waitForEnabled(); + await browser.clickVisible(Selectors.FavoriteSaveButton); + await browser.$(Selectors.FavoriteModal).waitForExist({ reverse: true }); + } +} + +export async function setupDefaultConnections(browser: CompassBrowser) { + /* + This is intended to be used by most test files (ones that don't care too much + about the intricacies about connections) in a before() hook after starting + compass. + + A beforeEach() hook can then use await browser.disconnectAll() to + disconnect all connections and use browser.connectToDefaults() to connect + to the existing connections without having to create them again via the + connection form. + + Then every test in that file starts with two connections that have the same + databases and collections. This forces tests to always encounter the "worst + case" where there are multiple connections connected and the database and + collection names are ambiguous. + + There is no good reason for this command to use the UI to create the + connections. It could also import them from a file, for example. Alternatively + we could have used the CLI to import connections from a file, but then that + affects the way we start compass and ties up the optional CLI parameters + whereas we do have some tests that try and use those. We can easily change + this in future if needed, though. + */ + + if (TEST_COMPASS_WEB) { + // we can't save connections in compass-web yet + return; + } + + for (const connectionName of [ + DEFAULT_CONNECTION_NAME_1, + DEFAULT_CONNECTION_NAME_2, + ]) { + if (await browser.removeConnection(connectionName)) { + debug('Removing existing connection so we do not create a duplicate', { + connectionName, + }); + } + } + + await browser.saveConnection( + { + connectionString: DEFAULT_CONNECTION_STRING_1, + // NOTE: no connectionName, we're going with the auto-generated one. Also no + // connectionColor. Passing a name and colour for single connection world, + // though, because that's the only way to create a favourite. + }, + DEFAULT_CONNECTION_NAME_1, + 'color1' + ); + + // no need for a second connection in single connection mode + if (TEST_MULTIPLE_CONNECTIONS) { + await browser.saveConnection( + { + connectionString: DEFAULT_CONNECTION_STRING_2, + // NOTE: filling in a name so that this one does _not_ have the auto-generated one + connectionName: DEFAULT_CONNECTION_NAME_2, + connectionColor: 'Iris', + }, + DEFAULT_CONNECTION_NAME_2, + 'color8' + ); + } +} diff --git a/packages/compass-e2e-tests/helpers/commands/connect-with-connection-form.ts b/packages/compass-e2e-tests/helpers/commands/connect-with-connection-form.ts deleted file mode 100644 index 7f862622192..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/connect-with-connection-form.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { CompassBrowser } from '../compass-browser'; -import type { ConnectFormState } from '../connect-form-state'; - -export async function connectWithConnectionForm( - browser: CompassBrowser, - options: ConnectFormState, - connectionStatus: 'success' | 'failure' | 'either' = 'success', - timeout?: number -): Promise { - await browser.disconnectAll(); - - // If a connectionName is specified and a connection already exists with this - // name, make sure we don't add a duplicate so that tests can always address - // this new connection. - if (options.connectionName) { - await browser.removeConnection(options.connectionName); - } - - await browser.setConnectFormState(options); - - await browser.doConnect(connectionStatus, timeout); -} diff --git a/packages/compass-e2e-tests/helpers/commands/connect-with-connection-string.ts b/packages/compass-e2e-tests/helpers/commands/connect-with-connection-string.ts deleted file mode 100644 index c2adc7117d3..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/connect-with-connection-string.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - DEFAULT_CONNECTION_STRING, - TEST_MULTIPLE_CONNECTIONS, - connectionNameFromString, -} from '../compass'; -import type { CompassBrowser } from '../compass-browser'; -import * as Selectors from '../selectors'; - -export async function connectWithConnectionString( - browser: CompassBrowser, - connectionString = DEFAULT_CONNECTION_STRING, - connectionStatus: 'success' | 'failure' | 'either' = 'success', - timeout?: number -): Promise { - await browser.disconnectAll(); - - if (TEST_MULTIPLE_CONNECTIONS) { - // if the modal is still animating away when we're connecting again, things - // are going to get confused - await browser - .$(Selectors.ConnectionModal) - .waitForDisplayed({ reverse: true }); - - // if a connection with this name already exists, remove it otherwise we'll - // add a duplicate and things will get complicated fast - const connectionName = connectionNameFromString(connectionString); - await browser.removeConnection(connectionName); - - await browser.clickVisible(Selectors.Multiple.SidebarNewConnectionButton); - await browser.$(Selectors.ConnectionModal).waitForDisplayed(); - } - - await browser.setValueVisible( - Selectors.ConnectionFormStringInput, - connectionString - ); - await browser.doConnect(connectionStatus, timeout); -} diff --git a/packages/compass-e2e-tests/helpers/commands/connect.ts b/packages/compass-e2e-tests/helpers/commands/connect.ts new file mode 100644 index 00000000000..975d628d460 --- /dev/null +++ b/packages/compass-e2e-tests/helpers/commands/connect.ts @@ -0,0 +1,233 @@ +import { + DEFAULT_CONNECTION_NAME_1, + DEFAULT_CONNECTION_NAME_2, + DEFAULT_CONNECTION_STRING_1, + TEST_COMPASS_WEB, + TEST_MULTIPLE_CONNECTIONS, + connectionNameFromString, +} from '../compass'; +import type { CompassBrowser } from '../compass-browser'; +import type { ConnectFormState } from '../connect-form-state'; +import * as Selectors from '../selectors'; +import Debug from 'debug'; +const debug = Debug('compass-e2e-tests'); + +export async function waitForConnectionScreen( + browser: CompassBrowser +): Promise { + // there isn't a separate connection screen in multiple connections, just a modal you can access at any time + if (TEST_MULTIPLE_CONNECTIONS) { + return; + } + + const selector = TEST_COMPASS_WEB + ? Selectors.ConnectionFormStringInput + : Selectors.ConnectSection; + const connectScreenElement = await browser.$(selector); + await connectScreenElement.waitForDisplayed(); +} + +export async function getConnectFormConnectionString( + browser: CompassBrowser, + shouldFocusInput = false +): Promise { + const inputElem = await browser.$(Selectors.ConnectionFormStringInput); + await inputElem.waitForDisplayed(); + if (shouldFocusInput) { + await browser.waitUntil(async () => { + await inputElem.click(); + return await inputElem.isFocused(); + }); + } + return await inputElem.getValue(); +} + +type ConnectionResultOptions = { + connectionStatus?: 'success' | 'failure' | 'either'; + timeout?: number; +}; + +type ConnectOptions = ConnectionResultOptions & { + removeConnections?: boolean; +}; + +export async function connectWithConnectionString( + browser: CompassBrowser, + connectionString = DEFAULT_CONNECTION_STRING_1, + options: ConnectOptions = {} +): Promise { + // Use this command when you need to add a new connection with a specific + // connection string. Most test files should just be using + // browser.connectToDefaults() + + if (TEST_MULTIPLE_CONNECTIONS) { + // if the modal is still animating away when we're connecting again, things + // are going to get confused + await browser + .$(Selectors.ConnectionModal) + .waitForDisplayed({ reverse: true }); + + // if a connection with this name already exists, remove it otherwise we'll + // add a duplicate and things will get complicated fast + const connectionName = connectionNameFromString(connectionString); + if (await browser.removeConnection(connectionName)) { + debug('Removing existing connection so we do not create a duplicate', { + connectionName, + }); + } + + await browser.clickVisible(Selectors.Multiple.SidebarNewConnectionButton); + await browser.$(Selectors.ConnectionModal).waitForDisplayed(); + } + + await browser.setValueVisible( + Selectors.ConnectionFormStringInput, + connectionString + ); + + const connectionName = connectionNameFromString(connectionString); + await browser.doConnect(connectionName, options); +} + +export async function connectWithConnectionForm( + browser: CompassBrowser, + state: ConnectFormState, + options: ConnectOptions = {} +): Promise { + // Use this command when you need to add a new connection with specific + // connect form field values. Most test files should just be using + // browser.connectToDefaults() + + // If a connectionName is specified and a connection already exists with this + // name, make sure we don't add a duplicate so that tests can always address + // this new connection. + if (state.connectionName) { + if (await browser.removeConnection(state.connectionName)) { + debug('Removing existing connection so we do not create a duplicate', { + connectionName: state.connectionName, + }); + } + } + + await browser.setConnectFormState(state); + + if (!state.connectionName) { + // In theory we could calculate the auto-generated connectionName here or + // try an read it out. + throw new Error('state.connectionName is required'); + } + const connectionName = state.connectionName; + await browser.doConnect(connectionName, options); +} + +export async function doConnect( + browser: CompassBrowser, + connectionName: string, + options: ConnectionResultOptions = {} +) { + await browser.clickVisible(Selectors.ConnectButton); + await browser.waitForConnectionResult(connectionName, options); +} + +export async function waitForConnectionResult( + browser: CompassBrowser, + connectionName: string, + { connectionStatus = 'success', timeout }: ConnectionResultOptions = {} +) { + const waitOptions = typeof timeout !== 'undefined' ? { timeout } : undefined; + + if (connectionStatus === 'either') { + // TODO(COMPASS-7600,COMPASS-8153): this doesn't support compass-web or + // multiple connections yet, but also isn't encountered yet For the rare + // cases where we don't care whether it fails or succeeds + await browser + .$(`${Selectors.DatabasesTable},${Selectors.ConnectionFormErrorMessage}`) + .waitForDisplayed(); + } else if (connectionStatus === 'success') { + // Wait for the first meaningful thing on the screen after being connected + // and assume that's a good enough indicator that we are connected to the + // server + if (TEST_COMPASS_WEB) { + // In compass-web, for now, we land on the Databases tab after connecting + await browser + .$('[data-testid="workspace-tab-button"][data-type=Databases]') + .waitForDisplayed(); + } else if (TEST_MULTIPLE_CONNECTIONS) { + if (await browser.$(Selectors.SidebarFilterInput).isDisplayed()) { + // Clear the filter to make sure every connection shows + await browser.clickVisible(Selectors.SidebarFilterInput); + await browser.setValueVisible(Selectors.SidebarFilterInput, ''); + } + await browser + .$( + Selectors.Multiple.connectionItemByName(connectionName, { + connected: true, + }) + ) + .waitForDisplayed(); + } else { + // In the single connection world we land on the My Queries page + await browser.$(Selectors.MyQueriesList).waitForDisplayed(); + } + } else if (connectionStatus === 'failure') { + if (TEST_MULTIPLE_CONNECTIONS) { + const element = await browser.$(Selectors.ConnectionToastErrorText); + await element.waitForDisplayed(waitOptions); + return await element.getText(); + } else { + // TODO(COMPASS-7600): this doesn't support compass-web yet, but also + // isn't encountered yet + const element = await browser.$(Selectors.ConnectionFormErrorMessage); + await element.waitForDisplayed(waitOptions); + return await element.getText(); + } + } else { + const exhaustiveCheck: never = connectionStatus; + throw new Error(`Unhandled connectionStatus case: ${exhaustiveCheck}`); + } + + if (TEST_MULTIPLE_CONNECTIONS) { + // make sure the placeholders for databases & collections that are loading are all gone + await browser + .$(Selectors.DatabaseCollectionPlaceholder) + .waitForDisplayed({ reverse: true }); + } +} + +export async function connectByName( + browser: CompassBrowser, + connectionName: string, + options: ConnectionResultOptions = {} +) { + await browser.clickVisible(Selectors.sidebarConnectionButton(connectionName)); + + if (!TEST_MULTIPLE_CONNECTIONS) { + // for single connections it only fills the connection form and we still + // have to click connect. For multiple connections clicking the connection + // connects + await browser.pause(1000); + await browser.clickVisible(Selectors.ConnectButton); + } + + await browser.waitForConnectionResult(connectionName, options); +} + +export async function connectToDefaults(browser: CompassBrowser) { + if (TEST_COMPASS_WEB) { + // we can't connect by name with compass-web because we can't save connections yet + await browser.connectWithConnectionString(); + return; + } + + // See setupDefaultConnections() for the details behind the thinking here. + await browser.connectByName(DEFAULT_CONNECTION_NAME_1); + + if (TEST_MULTIPLE_CONNECTIONS) { + await browser.connectByName(DEFAULT_CONNECTION_NAME_2); + } + + // We assume that we connected successfully, so just close the success toasts + // early to make sure they aren't in the way of tests. Tests that care about + // those toasts don't and shouldn't be using this command. + await browser.hideAllVisibleToasts(); +} diff --git a/packages/compass-e2e-tests/helpers/commands/disconnect.ts b/packages/compass-e2e-tests/helpers/commands/disconnect.ts index 881a442d2fe..3b04ebf3699 100644 --- a/packages/compass-e2e-tests/helpers/commands/disconnect.ts +++ b/packages/compass-e2e-tests/helpers/commands/disconnect.ts @@ -3,9 +3,71 @@ import type { CompassBrowser } from '../compass-browser'; import delay from '../delay'; import * as Selectors from '../selectors'; -import Debug from 'debug'; +async function disconnectAllWeb(browser: CompassBrowser): Promise { + const url = new URL(await browser.getUrl()); + url.pathname = '/'; + await browser.navigateTo(url.toString()); + const element = await browser.$(Selectors.ConnectionFormStringInput); + await element.waitForDisplayed(); +} + +async function disconnectAllSingle(browser: CompassBrowser) { + const cancelConnectionButtonElement = await browser.$( + Selectors.CancelConnectionButton + ); + // If we are still connecting, let's try cancelling the connection first + if (await cancelConnectionButtonElement.isDisplayed()) { + try { + await browser.closeConnectModal(); + } catch (e) { + // If that failed, the button was probably gone before we managed to + // click it. Let's go through the whole disconnecting flow now + } + } + + await delay(100); + + await browser.execute(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('electron').ipcRenderer.emit('app:disconnect'); + }); + + // for single connections we expect the connect screen to re-appear + await browser.$(Selectors.ConnectSection).waitForDisplayed(); + + // clear the form + await browser.clickVisible(Selectors.Single.SidebarNewConnectionButton); + await delay(100); +} + +async function resetForDisconnect( + browser: CompassBrowser, + { + closeToasts = true, + }: { + closeToasts?: boolean; + } = {} +) { + if (await browser.$(Selectors.LGModal).isDisplayed()) { + // close any modals that might be in the way + await browser.clickVisible(Selectors.LGModalClose); + await browser.$(Selectors.LGModal).waitForDisplayed({ reverse: true }); + } -const debug = Debug('compass-e2e-tests'); + // Collapse all the connections so that they will all hopefully fit on screen + // and therefore be rendered. + await browser.clickVisible(Selectors.CollapseConnectionsButton); + + if (await browser.$(Selectors.SidebarFilterInput).isDisplayed()) { + // Clear the filter to make sure every connection shows + await browser.clickVisible(Selectors.SidebarFilterInput); + await browser.setValueVisible(Selectors.SidebarFilterInput, ''); + } + + if (closeToasts) { + await browser.hideAllVisibleToasts(); + } +} export async function disconnectAll( browser: CompassBrowser, @@ -15,76 +77,65 @@ export async function disconnectAll( closeToasts?: boolean; } = {} ): Promise { + // This command is mostly intended for use inside a beforeEach() hook, + // probably in conjunction with browser.connectToDefaults() so that each test + // will start off with multiple connections already connected. + if (TEST_COMPASS_WEB) { - const url = new URL(await browser.getUrl()); - url.pathname = '/'; - await browser.navigateTo(url.toString()); - const element = await browser.$(Selectors.ConnectionFormStringInput); - await element.waitForDisplayed(); - return; + return await disconnectAllWeb(browser); } if (!TEST_MULTIPLE_CONNECTIONS) { - const cancelConnectionButtonElement = await browser.$( - Selectors.CancelConnectionButton + return await disconnectAllSingle(browser); + } + + // The previous test could have ended with modals and/or toasts left open and + // a search filter in the sidebar. Reset those so we can get to a known state. + await resetForDisconnect(browser, { closeToasts }); + + // The potential problem here is that the list is virtual, so it is possible + // that not every connection is rendered. Collapsing them all helps a little + // bit, though. + const connectionItems = await browser.$$( + Selectors.Multiple.ConnectedConnectionItems + ); + for (const connectionItem of connectionItems) { + const connectionName = await connectionItem.getAttribute( + 'data-connection-name' ); - // If we are still connecting, let's try cancelling the connection first - if (await cancelConnectionButtonElement.isDisplayed()) { - try { - await browser.closeConnectModal(); - } catch (e) { - // If that failed, the button was probably gone before we managed to - // click it. Let's go through the whole disconnecting flow now - } - } + await browser.disconnectByName(connectionName); + } - await delay(100); + if (closeToasts) { + // If we disconnected "too soon" and we get an error like "Failed to + // retrieve server info" or similar, there might be an error or warning + // toast by now. If so, just close it otherwise the next test or connection + // attempt will be confused by it. + await browser.hideAllVisibleToasts(); } - await browser.execute(() => { - // eslint-disable-next-line @typescript-eslint/no-var-requires - require('electron').ipcRenderer.emit('app:disconnect'); - }); + // NOTE: unlike the single connection flow this doesn't make sure the New + // Connection modal is open after disconnecting. + // This also doesn't remove all connections from the sidebar so the + // connection will still be there, just disconnected. +} - if (TEST_MULTIPLE_CONNECTIONS) { - // For multiple connections we're making the assumption that there should be - // no active connections left. Use a different command if you expect to - // disconnect just one connection and still keep others around. - await browser - .$(`${Selectors.SidebarTreeItems}[aria-expanded=true]`) - .waitForExist({ reverse: true }); - - // The potential problem here is that the list is virtual, so it is possible - // that not every connection is rendered and then this won't wait long - // enough. For now just wait an extra second just in case. - await browser.pause(1000); - - if (closeToasts) { - // If we disconnected "too soon" and we get an error like "Failed to - // retrieve server info" or similar, there might be an error or warning - // toast by now. If so, just close it otherwise the next test or connection - // attempt will be confused by it. - if (await browser.$(Selectors.LGToastCloseButton).isExisting()) { - try { - const toastText = await browser.$('#lg-toast-region').getText(); - debug('Closing toast', toastText); - await browser.clickVisible(Selectors.LGToastCloseButton); - } catch (error) { - debug('ignoring', error); - } - } - } +export async function disconnectByName( + browser: CompassBrowser, + connectionName: string +) { + await resetForDisconnect(browser, { closeToasts: false }); - // NOTE: unlike the single connection flow this doesn't make sure the New - // Connection modal is open after disconnecting. - // This also doesn't remove all connections from the sidebar so the - // connection will still be there, just disconnected. - } else { - // for single connections we expect the connect screen to re-appear - await browser.$(Selectors.ConnectSection).waitForDisplayed(); - - // clear the form - await browser.clickVisible(Selectors.Single.SidebarNewConnectionButton); - await delay(100); - } + await browser.selectConnectionMenuItem( + connectionName, + Selectors.Multiple.DisconnectConnectionItem + ); + + await browser + .$( + Selectors.Multiple.connectionItemByName(connectionName, { + connected: false, + }) + ) + .waitForDisplayed(); } diff --git a/packages/compass-e2e-tests/helpers/commands/do-connect.ts b/packages/compass-e2e-tests/helpers/commands/do-connect.ts deleted file mode 100644 index 899b364f7e2..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/do-connect.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { CompassBrowser } from '../compass-browser'; -import * as Selectors from '../selectors'; - -export async function doConnect( - browser: CompassBrowser, - connectionStatus: 'success' | 'failure' | 'either' = 'success', - timeout?: number -): Promise { - await browser.clickVisible(Selectors.ConnectButton); - await browser.waitForConnectionResult(connectionStatus, timeout); -} diff --git a/packages/compass-e2e-tests/helpers/commands/drop-namespace.ts b/packages/compass-e2e-tests/helpers/commands/drop-namespace.ts index f086614a64b..509a1ace615 100644 --- a/packages/compass-e2e-tests/helpers/commands/drop-namespace.ts +++ b/packages/compass-e2e-tests/helpers/commands/drop-namespace.ts @@ -14,8 +14,6 @@ export async function dropNamespace( const confirmButton = await browser.$(Selectors.DropNamespaceDropButton); await confirmButton.waitForEnabled(); - await browser.screenshot('drop-namespace-modal.png'); - await confirmButton.click(); const successToast = browser.$(Selectors.DropNamespaceSuccessToast); diff --git a/packages/compass-e2e-tests/helpers/commands/export-to-language.ts b/packages/compass-e2e-tests/helpers/commands/export-to-language.ts index 78525bcabf9..65ec37e70f4 100644 --- a/packages/compass-e2e-tests/helpers/commands/export-to-language.ts +++ b/packages/compass-e2e-tests/helpers/commands/export-to-language.ts @@ -65,8 +65,6 @@ export async function exportToLanguage( // buttons in the modal and it's hard to coordinate the flow to avoid const text = await browser.$(Selectors.ExportToLanguageQueryOutput).getText(); - await browser.screenshot('export-to-language-modal.png'); - // close the modal again await browser.clickVisible(Selectors.ExportToLanguageCloseButton); await exportModal.waitForDisplayed({ reverse: true }); diff --git a/packages/compass-e2e-tests/helpers/commands/get-connect-form-connection-string.ts b/packages/compass-e2e-tests/helpers/commands/get-connect-form-connection-string.ts deleted file mode 100644 index b9400b195dc..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/get-connect-form-connection-string.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { CompassBrowser } from '../compass-browser'; -import * as Selectors from '../selectors'; - -export async function getConnectFormConnectionString( - browser: CompassBrowser, - shouldFocusInput = false -): Promise { - const inputElem = await browser.$(Selectors.ConnectionFormStringInput); - await inputElem.waitForDisplayed(); - if (shouldFocusInput) { - await browser.waitUntil(async () => { - await inputElem.click(); - return await inputElem.isFocused(); - }); - } - return await inputElem.getValue(); -} diff --git a/packages/compass-e2e-tests/helpers/commands/get-connect-form-state.ts b/packages/compass-e2e-tests/helpers/commands/get-connect-form-state.ts deleted file mode 100644 index 22a5480735d..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/get-connect-form-state.ts +++ /dev/null @@ -1,377 +0,0 @@ -import type { CompassBrowser } from '../compass-browser'; -import * as Selectors from '../selectors'; -import type { ConnectFormState } from '../connect-form-state'; -import { TEST_MULTIPLE_CONNECTIONS } from '../compass'; - -export async function getConnectFormState( - browser: CompassBrowser, - isFocused = false -): Promise { - const wasExpanded = await browser.expandAccordion( - Selectors.ConnectionFormAdvancedToggle - ); - - const connectionString = await browser.getConnectFormConnectionString( - isFocused - ); - - // General - const initialTab = await browser.navigateToConnectTab('General'); - - const defaultPromises: Record> = { - scheme: getCheckedRadioValue(browser, Selectors.ConnectionFormSchemeRadios), - hosts: getMultipleValues(browser, Selectors.ConnectionFormHostInputs), - directConnection: getCheckboxValue( - browser, - Selectors.ConnectionFormDirectConnectionCheckbox - ), - }; - if (TEST_MULTIPLE_CONNECTIONS) { - defaultPromises.connectionName = getValue( - browser, - Selectors.ConnectionFormConnectionName - ); - defaultPromises.connectionColor = getValue( - browser, - Selectors.ConnectionFormConnectionColor - ); - defaultPromises.connectionFavorite = getCheckboxValue( - browser, - Selectors.ConnectionFormFavoriteCheckbox - ); - } - const defaultState = await promiseMap(defaultPromises); - - // Authentication - await browser.navigateToConnectTab('Authentication'); - const authenticationState = await promiseMap({ - authMethod: getCheckedRadioValue( - browser, - Selectors.ConnectionFormAuthenticationMethodRadios - ), - - // Username/Password - defaultUsername: getValue(browser, Selectors.ConnectionFormInputUsername), - defaultPassword: getValue(browser, Selectors.ConnectionFormInputPassword), - defaultAuthSource: getValue( - browser, - Selectors.ConnectionFormInputAuthSource - ), - defaultAuthMechanism: getCheckedRadioValue( - browser, - Selectors.ConnectionFormAuthMechanismRadios - ), - - // OIDC - oidcUsername: getValue(browser, Selectors.ConnectionFormInputOIDCUsername), - - // Kerberos - kerberosPrincipal: getValue( - browser, - Selectors.ConnectionFormInputGssApiPrincipal - ), - kerberosServiceName: getValue( - browser, - Selectors.ConnectionFormInputGssApiServiceName - ), - kerberosCanonicalizeHostname: getCheckedRadioValue( - browser, - Selectors.ConnectionFormCanonicalizeHostNameRadios - ), - kerberosServiceRealm: getValue( - browser, - Selectors.ConnectionFormInputGssApiServiceRealm - ), - kerberosProvidePassword: getCheckboxValue( - browser, - Selectors.ConnectionFormGssApiPasswordCheckbox - ), - kerberosPassword: getValue( - browser, - Selectors.ConnectionFormInputGssApiPassword - ), - // LDAP - ldapUsername: getValue(browser, Selectors.ConnectionFormInputPlainUsername), - ldapPassword: getValue(browser, Selectors.ConnectionFormInputPlainPassword), - - // AWS IAM - awsAccessKeyId: getValue( - browser, - Selectors.ConnectionFormInputAWSAccessKeyId - ), - awsSecretAccessKey: getValue( - browser, - Selectors.ConnectionFormInputAWSSecretAccessKey - ), - awsSessionToken: getValue( - browser, - Selectors.ConnectionFormInputAWSSessionToken - ), - }); - - // TLS/SSL - await browser.navigateToConnectTab('TLS/SSL'); - const tlsState = await promiseMap({ - sslConnection: getCheckedRadioValue( - browser, - Selectors.ConnectionFormSSLConnectionRadios - ), - - // these are just the button text, not actually the file path - tlsCAFile: getFilename(browser, Selectors.ConnectionFormTlsCaButton), - tlsCertificateKeyFile: getFilename( - browser, - Selectors.ConnectionFormTlsCertificateKeyButton - ), - - clientKeyPassword: getValue( - browser, - Selectors.ConnectionFormInputTlsCertificateKeyFilePassword - ), - tlsInsecure: getCheckboxValue( - browser, - Selectors.ConnectionFormTlsInsecureCheckbox - ), - tlsAllowInvalidHostnames: getCheckboxValue( - browser, - Selectors.ConnectionFormTlsAllowInvalidHostnamesCheckbox - ), - tlsAllowInvalidCertificates: getCheckboxValue( - browser, - Selectors.ConnectionFormTlsAllowInvalidCertificatesCheckbox - ), - useSystemCA: getCheckboxValue( - browser, - Selectors.ConnectionFormTlsUseSystemCACheckbox - ), - }); - - // Proxy/SSH - await browser.navigateToConnectTab('Proxy/SSH'); - - const proxyState = await promiseMap({ - proxyMethod: getCheckedRadioValue( - browser, - Selectors.ConnectionFormProxyMethodRadios - ), - - // SSH with Password - // NOTE: these don't go in the URI so will likely never return values - sshPasswordHost: getValue( - browser, - Selectors.ConnectionFormInputSshPasswordHost - ), - sshPasswordPort: getValue( - browser, - Selectors.ConnectionFormInputSshPasswordPort - ), - sshPasswordUsername: getValue( - browser, - Selectors.ConnectionFormInputSshPasswordUsername - ), - sshPasswordPassword: getValue( - browser, - Selectors.ConnectionFormInputSshPasswordPassword - ), - - // SSH with Identity File - // NOTE: same as above these are unlikely ever return values in these tests - sshIdentityHost: getValue( - browser, - Selectors.ConnectionFormInputSshIdentityHost - ), - sshIdentityPort: getValue( - browser, - Selectors.ConnectionFormInputSshIdentityPort - ), - sshIdentityUsername: getValue( - browser, - Selectors.ConnectionFormInputSshIdentityUsername - ), - // same as above: this is the button text, not the file path - sshIdentityKeyFile: getFilename( - browser, - Selectors.ConnectionFormSshIdentityKeyButton - ), - sshIdentityPassword: getValue( - browser, - Selectors.ConnectionFormInputSshIdentityPassword - ), - - socksHost: getValue(browser, Selectors.ConnectionFormInputSocksHost), - socksPort: getValue(browser, Selectors.ConnectionFormInputSocksPort), - socksUsername: getValue( - browser, - Selectors.ConnectionFormInputSocksUsername - ), - socksPassword: getValue( - browser, - Selectors.ConnectionFormInputSocksPassword - ), - }); - - // FLE2 - await browser.navigateToConnectTab('In-Use Encryption'); - await browser.expandAccordion(Selectors.ConnectionFormInputFLELocalKMS); - - const inUseEncryptionState = await promiseMap({ - fleKeyVaultNamespace: getValue( - browser, - Selectors.ConnectionFormInputFLEKeyVaultNamespace - ), - fleStoreCredentials: getCheckboxValue( - browser, - Selectors.ConnectionFormInputFLEStoreCredentialsCheckbox - ), - fleKey: getValue(browser, Selectors.ConnectionFormInputFLELocalKMS), - fleEncryptedFieldsMap: browser.getCodemirrorEditorText( - Selectors.ConnectionFormInputFLEEncryptedFieldsMapEditor - ), - }); - - // Advanced - await browser.navigateToConnectTab('Advanced'); - const advancedState = await promiseMap({ - readPreference: getCheckedRadioValue( - browser, - Selectors.ConnectionFormReadPreferenceRadios - ), - replicaSet: getValue(browser, Selectors.ConnectionFormInputReplicaset), - defaultDatabase: getValue( - browser, - Selectors.ConnectionFormInputDefaultDatabase - ), - urlOptionKeys: getMultipleValues( - browser, - Selectors.ConnectionFormUrlOptionKeys - ), - urlOptionValues: getMultipleValues( - browser, - Selectors.ConnectionFormUrlOptionValues - ), - }); - - if (advancedState.urlOptionKeys) { - advancedState.urlOptions = Object.fromEntries( - advancedState.urlOptionKeys.map((k: string, i: number) => [ - k, - advancedState.urlOptionValues[i], - ]) - ); - - delete advancedState.urlOptionKeys; - delete advancedState.urlOptionValues; - } - - const result = { - connectionString, - ...defaultState, - ...authenticationState, - ...tlsState, - ...proxyState, - ...advancedState, - ...inUseEncryptionState, - }; - - // restore the initial state - if (wasExpanded) { - // get back to the tab it was on - await browser.navigateToConnectTab(initialTab); - } else { - // collapse it again - await browser.clickVisible(Selectors.ConnectionFormAdvancedToggle); - - await browser.waitUntil(async () => { - const advancedButton = await browser.$( - Selectors.ConnectionFormAdvancedToggle - ); - return (await advancedButton.getAttribute('aria-expanded')) === 'false'; - }); - } - - return result; -} - -async function getCheckedRadioValue( - browser: CompassBrowser, - selector: string -): Promise { - const elements = await browser.$$(selector); - for (const element of elements) { - if (await element.isSelected()) { - return element.getValue(); - } - } - - return null; -} - -async function getCheckboxValue( - browser: CompassBrowser, - selector: string -): Promise { - const element = await browser.$(selector); - if (!(await element.isExisting())) { - return null; // as opposed to true for checked and false for not - } - - return element.isSelected(); -} - -async function getText( - browser: CompassBrowser, - selector: string -): Promise { - const element = await browser.$(selector); - if (!(await element.isExisting())) { - return null; - } - - const text = await element.getText(); - return text || null; -} - -async function getFilename( - browser: CompassBrowser, - selector: string -): Promise { - const text = await getText(browser, selector); - return text === 'Select a file...' ? null : text; -} - -async function getValue( - browser: CompassBrowser, - selector: string -): Promise { - const element = await browser.$(selector); - if (!(await element.isExisting())) { - return null; - } - - const value = await element.getValue(); - return value || null; -} - -async function getMultipleValues( - browser: CompassBrowser, - selector: string -): Promise { - const results = ( - await browser.$$(selector).map((element) => element.getValue()) - ).filter((result) => result !== ''); - - return results.length ? results : null; -} - -interface NamedPromises { - [key: string]: Promise; -} - -async function promiseMap(map: NamedPromises) { - const results = await Promise.all(Object.values(map)); - return Object.fromEntries( - Object.keys(map) - .map((k, i) => [k, results[i]]) - .filter(([, v]) => v !== null) - ); -} diff --git a/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts b/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts index 9a2a9ad13c9..49648e2bb54 100644 --- a/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts +++ b/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts @@ -1,6 +1,10 @@ import type { CompassBrowser } from '../compass-browser'; import * as Selectors from '../selectors'; +import Debug from 'debug'; + +const debug = Debug('compass-e2e-tests'); + export async function hideAllVisibleToasts( browser: CompassBrowser ): Promise { @@ -18,6 +22,7 @@ export async function hideAllVisibleToasts( if (!isToastVisible) { continue; } + debug('closing toast', await toast.getAttribute('data-testid')); await browser.clickVisible(toast.$(Selectors.LGToastCloseButton)); await toast.waitForExist({ reverse: true }); } catch (err) { diff --git a/packages/compass-e2e-tests/helpers/commands/index.ts b/packages/compass-e2e-tests/helpers/commands/index.ts index cac8d754db0..19180e48e88 100644 --- a/packages/compass-e2e-tests/helpers/commands/index.ts +++ b/packages/compass-e2e-tests/helpers/commands/index.ts @@ -1,12 +1,11 @@ export * from './exists-eventually'; export * from './click-visible'; export * from './set-value-visible'; -export * from './wait-for-connection-screen'; export * from './close-connect-modal'; export * from './close-settings-modal'; export * from './close-welcome-modal'; -export * from './connect-with-connection-string'; -export * from './connect-with-connection-form'; +export * from './connect'; +export * from './connect-form'; export * from './disconnect'; export * from './shell-eval'; export * from './connection-workspaces'; @@ -21,7 +20,6 @@ export * from './wait-for-animations'; export * from './wait-for-aria-disabled'; export * from './listen-for-telemetry-events'; export * from './select-file'; -export * from './do-connect'; export * from './hover'; export * from './add-database'; export * from './add-collection'; @@ -30,11 +28,8 @@ export * from './get-query-id'; export * from './run-find'; export * from './export-to-language'; export * from './click-parent'; -export * from './get-connect-form-state'; -export * from './set-connect-form-state'; export * from './navigate-to-connect-tab'; export * from './expand-accordion'; -export * from './reset-connect-form'; export * from './scroll-to-virtual-item'; export * from './set-export-filename'; export * from './get-feature'; @@ -44,7 +39,6 @@ export * from './save-connection-string-as-favorite'; export * from './sidebar-connection'; export * from './select-connections-menu-item'; export * from './open-settings-modal'; -export * from './wait-for-connection-result'; export * from './screenshot'; export * from './open-shell'; export * from './close-shell'; @@ -60,7 +54,6 @@ export * from './toggle-aggregation-side-panel'; export * from './add-wizard'; export * from './set-combo-box-value'; export * from './wait-for-export-to-finish'; -export * from './get-connect-form-connection-string'; export * from './create-index'; export * from './drop-index'; export * from './hide-index'; diff --git a/packages/compass-e2e-tests/helpers/commands/reset-connect-form.ts b/packages/compass-e2e-tests/helpers/commands/reset-connect-form.ts deleted file mode 100644 index f37046e769d..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/reset-connect-form.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { TEST_MULTIPLE_CONNECTIONS } from '../compass'; -import type { CompassBrowser } from '../compass-browser'; -import * as Selectors from '../selectors'; - -export async function resetConnectForm(browser: CompassBrowser): Promise { - const Sidebar = TEST_MULTIPLE_CONNECTIONS - ? Selectors.Multiple - : Selectors.Single; - - if (TEST_MULTIPLE_CONNECTIONS) { - if (await browser.$(Selectors.ConnectionModal).isDisplayed()) { - await browser.clickVisible(Selectors.ConnectionModalCloseButton); - await browser - .$(Selectors.ConnectionModal) - .waitForDisplayed({ reverse: true }); - } - } - - await browser.clickVisible(Sidebar.SidebarNewConnectionButton); - - const connectionTitleSelector = TEST_MULTIPLE_CONNECTIONS - ? Selectors.ConnectionModalTitle - : Selectors.ConnectionTitle; - - const connectionTitle = await browser.$(connectionTitleSelector); - await connectionTitle.waitUntil(async () => { - return (await connectionTitle.getText()) === 'New Connection'; - }); - - await browser.waitUntil(async () => { - return ( - (await browser.getConnectFormConnectionString(true)) === - 'mongodb://localhost:27017' - ); - }); -} diff --git a/packages/compass-e2e-tests/helpers/commands/save-favorite.ts b/packages/compass-e2e-tests/helpers/commands/save-favorite.ts index e2a31a0120e..0921a0a7b8b 100644 --- a/packages/compass-e2e-tests/helpers/commands/save-favorite.ts +++ b/packages/compass-e2e-tests/helpers/commands/save-favorite.ts @@ -34,9 +34,6 @@ export async function saveFavorite( expect(await browser.$(Selectors.FavoriteSaveButton).getText()).to.equal( 'Save' ); - - await browser.screenshot('save-favorite-modal.png'); - await browser.clickVisible(Selectors.FavoriteSaveButton); await browser.$(Selectors.FavoriteModal).waitForExist({ reverse: true }); } diff --git a/packages/compass-e2e-tests/helpers/commands/set-connect-form-state.ts b/packages/compass-e2e-tests/helpers/commands/set-connect-form-state.ts deleted file mode 100644 index 3e52bedbc3f..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/set-connect-form-state.ts +++ /dev/null @@ -1,442 +0,0 @@ -import _ from 'lodash'; -import { expect } from 'chai'; -import type { CompassBrowser } from '../compass-browser'; -import * as Selectors from '../selectors'; -import type { ConnectFormState } from '../connect-form-state'; -import { TEST_MULTIPLE_CONNECTIONS } from '../compass'; - -async function waitForElementAnimations(browser: CompassBrowser, element: any) { - let previousResult = { - ...(await element.getLocation()), - ...(await element.getSize()), - }; - await browser.waitUntil(async function () { - // small delay to make sure that if it is busy animating it had time to move - // before the first check and between each two checks - await browser.pause(50); - - const result = { - ...(await element.getLocation()), - ...(await element.getSize()), - }; - const stopped = _.isEqual(result, previousResult); - previousResult = result; - return stopped; - }); -} - -const colorMap: Record = { - 'no-color': 'No Color', - color1: 'Red', - color2: 'Pink', - color3: 'Orange', - color4: 'Yellow', - color5: 'Green', - color6: 'Teal', - color7: 'Blue', - color8: 'Iris', - color9: 'Purple', -}; - -function colorValueToName(color: string): string { - if (colorMap[color]) { - return colorMap[color]; - } - return color; -} - -export async function setConnectFormState( - browser: CompassBrowser, - state: ConnectFormState -): Promise { - await browser.resetConnectForm(); - - await browser.expandAccordion(Selectors.ConnectionFormAdvancedToggle); - - // General - await browser.navigateToConnectTab('General'); - - if (state.scheme) { - await browser.clickParent( - Selectors.connectionFormSchemeRadio(state.scheme) - ); - } - if (state.hosts) { - for (let i = 0; i < state.hosts.length; ++i) { - if (i > 0) { - await browser.clickVisible( - '[data-testid="connection-add-host-button"]:last-child' - ); - } - await browser.setValueVisible( - `#connection-host-input-${i}`, - state.hosts[i] - ); - } - } - if (state.directConnection) { - await browser.clickParent(Selectors.ConnectionFormDirectConnectionCheckbox); - } - - if (TEST_MULTIPLE_CONNECTIONS) { - // Name, Color, Favorite - if (state.connectionName) { - await browser.setValueVisible( - Selectors.ConnectionFormConnectionName, - state.connectionName - ); - } - - if (state.connectionColor) { - await browser.selectOption( - Selectors.ConnectionFormConnectionColor, - colorValueToName(state.connectionColor) - ); - } - - if (state.connectionFavorite) { - await browser.clickParent(Selectors.ConnectionFormFavoriteCheckbox); - } - } - - // Authentication - await browser.navigateToConnectTab('Authentication'); - - if (state.authMethod) { - await browser.clickParent( - Selectors.connectionFormAuthenticationMethodRadio(state.authMethod) - ); - } - - // Username/Password - if (state.defaultUsername && state.defaultPassword) { - await browser.setValueVisible( - Selectors.ConnectionFormInputUsername, - state.defaultUsername - ); - await browser.setValueVisible( - Selectors.ConnectionFormInputPassword, - state.defaultPassword - ); - } - if (state.defaultAuthSource) { - await browser.setValueVisible( - Selectors.ConnectionFormInputAuthSource, - state.defaultAuthSource - ); - } - if (state.defaultAuthMechanism) { - await browser.clickParent( - Selectors.connectionFormAuthMechanismRadio(state.defaultAuthMechanism) - ); - } - - // Kerberos - if (state.kerberosPrincipal) { - await browser.setValueVisible( - Selectors.ConnectionFormInputGssApiPrincipal, - state.kerberosPrincipal - ); - } - if (state.kerberosServiceName) { - await browser.setValueVisible( - Selectors.ConnectionFormInputGssApiServiceName, - state.kerberosServiceName - ); - } - if (state.kerberosCanonicalizeHostname) { - await browser.clickParent( - Selectors.connectionFormCanonicalizeHostNameRadio( - state.kerberosCanonicalizeHostname - ) - ); - } - if (state.kerberosServiceRealm) { - await browser.setValueVisible( - Selectors.ConnectionFormInputGssApiServiceRealm, - state.kerberosServiceRealm - ); - } - if (state.kerberosProvidePassword) { - await browser.clickParent(Selectors.ConnectionFormGssApiPasswordCheckbox); - } - if (state.kerberosPassword) { - await browser.setValueVisible( - Selectors.ConnectionFormInputGssApiPassword, - state.kerberosPassword - ); - } - - // LDAP - if (state.ldapUsername && state.ldapPassword) { - await browser.setValueVisible( - Selectors.ConnectionFormInputPlainUsername, - state.ldapUsername - ); - await browser.setValueVisible( - Selectors.ConnectionFormInputPlainPassword, - state.ldapPassword - ); - } - - // AWS IAM - if (state.awsAccessKeyId && state.awsSecretAccessKey) { - await browser.setValueVisible( - Selectors.ConnectionFormInputAWSAccessKeyId, - state.awsAccessKeyId - ); - await browser.setValueVisible( - Selectors.ConnectionFormInputAWSSecretAccessKey, - state.awsSecretAccessKey - ); - } - if (state.awsSessionToken) { - await browser.setValueVisible( - Selectors.ConnectionFormInputAWSSessionToken, - state.awsSessionToken - ); - } - if (state.awsSessionToken) { - await browser.setValueVisible( - Selectors.ConnectionFormInputAWSSessionToken, - state.awsSessionToken - ); - } - - // OIDC - if (state.oidcUsername) { - await browser.setValueVisible( - Selectors.ConnectionFormInputOIDCUsername, - state.oidcUsername - ); - } - - // FLE2 - await browser.navigateToConnectTab('In-Use Encryption'); - - if (state.fleKeyVaultNamespace) { - await browser.setValueVisible( - Selectors.ConnectionFormInputFLEKeyVaultNamespace, - state.fleKeyVaultNamespace - ); - } - if (state.fleKey) { - await browser.expandAccordion(Selectors.ConnectionFormInputFLELocalKMS); - await browser.setValueVisible( - Selectors.ConnectionFormInputFLELocalKey, - state.fleKey - ); - } - if (state.fleEncryptedFieldsMap) { - // set the text in the editor - await browser.setCodemirrorEditorValue( - Selectors.ConnectionFormInputFLEEncryptedFieldsMapEditor, - state.fleEncryptedFieldsMap - ); - } - - // TLS/SSL - await browser.navigateToConnectTab('TLS/SSL'); - - if (state.sslConnection) { - await browser.clickParent( - Selectors.connectionFormSSLConnectionRadio(state.sslConnection) - ); - } - if (state.tlsCAFile) { - await browser.selectFile( - Selectors.ConnectionFormTlsCaFile, - state.tlsCAFile - ); - } - if (state.tlsCertificateKeyFile) { - await browser.selectFile( - Selectors.ConnectionFormTlsCertificateKeyFile, - state.tlsCertificateKeyFile - ); - } - if (state.clientKeyPassword) { - await browser.setValueVisible( - Selectors.ConnectionFormInputTlsCertificateKeyFilePassword, - state.clientKeyPassword - ); - } - if (state.tlsInsecure) { - await browser.clickParent(Selectors.ConnectionFormTlsInsecureCheckbox); - } - if (state.tlsAllowInvalidHostnames) { - await browser.clickParent( - Selectors.ConnectionFormTlsAllowInvalidHostnamesCheckbox - ); - } - if (state.tlsAllowInvalidCertificates) { - await browser.clickParent( - Selectors.ConnectionFormTlsAllowInvalidCertificatesCheckbox - ); - } - if (state.useSystemCA) { - await browser.clickParent(Selectors.ConnectionFormTlsUseSystemCACheckbox); - } - - // Proxy/SSH - await browser.navigateToConnectTab('Proxy/SSH'); - - //proxyMethod - if (state.proxyMethod) { - await browser.clickParent( - Selectors.connectionFormProxyMethodRadio(state.proxyMethod) - ); - } - - // SSH with Password - // NOTE: these don't affect the URI - if (state.sshPasswordHost && state.sshPasswordPort) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSshPasswordHost, - state.sshPasswordHost - ); - await browser.setValueVisible( - Selectors.ConnectionFormInputSshPasswordPort, - state.sshPasswordPort - ); - } - if (state.sshPasswordUsername) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSshPasswordUsername, - state.sshPasswordUsername - ); - } - if (state.sshPasswordPassword) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSshPasswordPassword, - state.sshPasswordPassword - ); - } - - // SSH with Identity File - // NOTE: these don't affect the URI - if (state.sshIdentityHost && state.sshIdentityPort) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSshIdentityHost, - state.sshIdentityHost - ); - await browser.setValueVisible( - Selectors.ConnectionFormInputSshIdentityPort, - state.sshIdentityPort - ); - } - if (state.sshIdentityUsername) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSshIdentityUsername, - state.sshIdentityUsername - ); - } - if (state.sshIdentityKeyFile) { - await browser.selectFile( - Selectors.ConnectionFormSshIdentityKeyFile, - state.sshIdentityKeyFile - ); - } - if (state.sshIdentityPassword) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSshIdentityPassword, - state.sshIdentityPassword - ); - } - - // Socks5 - if (state.socksHost) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSocksHost, - state.socksHost - ); - } - if (state.socksPort) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSocksPort, - state.socksPort - ); - } - if (state.socksUsername) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSocksUsername, - state.socksUsername - ); - } - if (state.socksPassword) { - await browser.setValueVisible( - Selectors.ConnectionFormInputSocksPassword, - state.socksPassword - ); - } - - // Advanced - await browser.navigateToConnectTab('Advanced'); - - if (state.readPreference) { - await browser.clickParent( - Selectors.connectionFormReadPreferenceRadio(state.readPreference) - ); - } - if (state.replicaSet) { - await browser.setValueVisible( - Selectors.ConnectionFormInputReplicaset, - state.replicaSet - ); - } - if (state.defaultDatabase) { - await browser.setValueVisible( - Selectors.ConnectionFormInputDefaultDatabase, - state.defaultDatabase - ); - } - if (state.urlOptions) { - for (const [index, [key, value]] of Object.entries( - state.urlOptions - ).entries()) { - // key - await browser.clickVisible( - Selectors.connectionFormUrlOptionKeyButton(index) - ); - - let found = false; - let allText: string[] = []; - - // for whatever reasons sometimes the first one or two come through as empty strings - await browser.waitUntil(async () => { - allText = []; - const options = await browser.$$('#select-key-menu [role="option"]'); - for (const option of options) { - const _text = await option.getText(); - const text = _text.trim(); - allText.push(text); - if (text === key) { - found = true; - await option.scrollIntoView(); - await option.waitForDisplayed(); - await waitForElementAnimations(browser, option); - await option.click(); - break; - } - } - return found; - }); - - // make sure we found and clicked on an option - expect( - found, - `Could not find URL option "${key}". Found "${allText.join(', ')}"` - ).to.be.true; - - // make sure the menu goes away once we clicked on the option - const menu = await browser.$('#select-key-menu'); - await menu.waitForExist({ reverse: true }); - - // value - await browser.setValueVisible( - Selectors.connectionFormUrlOptionValueInput(index), - value - ); - } - } -} diff --git a/packages/compass-e2e-tests/helpers/commands/set-value-visible.ts b/packages/compass-e2e-tests/helpers/commands/set-value-visible.ts index 26f93cde9ef..558c0145675 100644 --- a/packages/compass-e2e-tests/helpers/commands/set-value-visible.ts +++ b/packages/compass-e2e-tests/helpers/commands/set-value-visible.ts @@ -28,10 +28,6 @@ export async function setValueVisible( await element.setValue(value); // basically clearValue() then addValue() const actualValue = (await element.getValue()) ?? ''; - if (actualValue !== value) { - console.log(actualValue, '!==', value); - await browser.screenshot('setValueVisible.png'); - } return actualValue === value; }); } diff --git a/packages/compass-e2e-tests/helpers/commands/sidebar-connection.ts b/packages/compass-e2e-tests/helpers/commands/sidebar-connection.ts index 3bec991980a..3f5808d9dbb 100644 --- a/packages/compass-e2e-tests/helpers/commands/sidebar-connection.ts +++ b/packages/compass-e2e-tests/helpers/commands/sidebar-connection.ts @@ -104,12 +104,34 @@ export async function selectConnectionMenuItem( await browser.clickVisible(itemSelector); } +// TODO(COMPASS-8023): Just remove this once the single connection code is removed +async function removeConnectionSingle( + browser: CompassBrowser, + connectionName: string +): Promise { + const selector = Selectors.sidebarConnection(connectionName); + + if (await browser.$(selector).isExisting()) { + await browser.selectConnection(connectionName); + + await browser.selectConnectionMenuItem( + connectionName, + Selectors.Single.RemoveConnectionItem + ); + + await browser.$(selector).waitForDisplayed({ reverse: true }); + return true; + } + + return false; +} + export async function removeConnection( browser: CompassBrowser, connectionName: string -): Promise { +): Promise { if (!TEST_MULTIPLE_CONNECTIONS) { - return; + return await removeConnectionSingle(browser, connectionName); } // make sure there's no filter because if the connection is not displayed then we can't remove it @@ -129,7 +151,9 @@ export async function removeConnection( Selectors.Multiple.RemoveConnectionItem ); await browser.$(selector).waitForExist({ reverse: true }); + return true; } + return false; } export async function hasConnectionMenuItem( diff --git a/packages/compass-e2e-tests/helpers/commands/wait-for-connection-result.ts b/packages/compass-e2e-tests/helpers/commands/wait-for-connection-result.ts deleted file mode 100644 index 47798c3d9d4..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/wait-for-connection-result.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { TEST_COMPASS_WEB, TEST_MULTIPLE_CONNECTIONS } from '../compass'; -import type { CompassBrowser } from '../compass-browser'; -import * as Selectors from '../selectors'; - -export async function waitForConnectionResult( - browser: CompassBrowser, - connectionStatus: 'success' | 'failure' | 'either' = 'success', - timeout?: number -): Promise { - let selector: string; - if (connectionStatus === 'either') { - // TODO(COMPASS-7600): this doesn't support compass-web yet, but also isn't - // encountered yet For the rare cases where we don't care whether it fails - // or succeeds - selector = `${Selectors.DatabasesTable},${Selectors.ConnectionFormErrorMessage}`; - } else if (connectionStatus === 'success') { - // First meaningful thing on the screen after being connected, good enough - // indicator that we are connected to the server - // TODO(COMPASS-8023): wait for the specific connection to appear in the - // sidebar and be connected - selector = TEST_COMPASS_WEB - ? '[data-testid="workspace-tab-button"][data-type="Databases"]' - : TEST_MULTIPLE_CONNECTIONS - ? `${Selectors.SidebarTreeItems}[aria-expanded=true]` - : Selectors.MyQueriesList; - } else { - // TODO(COMPASS-7600): this doesn't support compass-web yet, but also isn't - // encountered yet - selector = TEST_MULTIPLE_CONNECTIONS - ? Selectors.ConnectionToastErrorText - : Selectors.ConnectionFormErrorMessage; - } - const element = await browser.$(selector); - await element.waitForDisplayed( - typeof timeout !== 'undefined' ? { timeout } : undefined - ); - if (connectionStatus === 'failure') { - return await element.getText(); - } - - if (TEST_MULTIPLE_CONNECTIONS) { - // make sure the placeholders for databases & collections that are loading are all gone - await browser - .$(Selectors.DatabaseCollectionPlaceholder) - .waitForDisplayed({ reverse: true }); - } -} diff --git a/packages/compass-e2e-tests/helpers/commands/wait-for-connection-screen.ts b/packages/compass-e2e-tests/helpers/commands/wait-for-connection-screen.ts deleted file mode 100644 index fa2ff12dec3..00000000000 --- a/packages/compass-e2e-tests/helpers/commands/wait-for-connection-screen.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TEST_COMPASS_WEB, TEST_MULTIPLE_CONNECTIONS } from '../compass'; -import type { CompassBrowser } from '../compass-browser'; -import * as Selectors from '../selectors'; - -export async function waitForConnectionScreen( - browser: CompassBrowser -): Promise { - // there isn't a separate connection screen in multiple connections, just a modal you can access at any time - if (TEST_MULTIPLE_CONNECTIONS) { - return; - } - - const selector = TEST_COMPASS_WEB - ? Selectors.ConnectionFormStringInput - : Selectors.ConnectSection; - const connectScreenElement = await browser.$(selector); - await connectScreenElement.waitForDisplayed(); -} diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index ed9c4cd41c1..b03ef09fa5a 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -76,11 +76,20 @@ export const MONGODB_TEST_SERVER_PORT = Number( process.env.MONGODB_TEST_SERVER_PORT ?? 27091 ); -export const DEFAULT_CONNECTION_STRING = `mongodb://127.0.0.1:${MONGODB_TEST_SERVER_PORT}/test`; -export const DEFAULT_CONNECTION_NAME = connectionNameFromString( - DEFAULT_CONNECTION_STRING +export const DEFAULT_CONNECTION_STRING_1 = `mongodb://127.0.0.1:${MONGODB_TEST_SERVER_PORT}/test`; +// NOTE: in browser.setupDefaultConnections() we don't give the first connection an +// explicit name, so it gets a calculated one based off the connection string +export const DEFAULT_CONNECTION_NAME_1 = connectionNameFromString( + DEFAULT_CONNECTION_STRING_1 ); +// for testing multiple connections +export const DEFAULT_CONNECTION_STRING_2 = `mongodb://127.0.0.1:${ + MONGODB_TEST_SERVER_PORT + 1 +}/test`; +// NOTE: in browser.setupDefaultConnections() the second connection gets given an explicit name +export const DEFAULT_CONNECTION_NAME_2 = 'connection-2'; + export function updateMongoDBServerInfo() { try { const { stdout, stderr } = crossSpawn.sync( diff --git a/packages/compass-e2e-tests/helpers/insert-data.ts b/packages/compass-e2e-tests/helpers/insert-data.ts index 0be1686ab23..8a2cdd18a2f 100644 --- a/packages/compass-e2e-tests/helpers/insert-data.ts +++ b/packages/compass-e2e-tests/helpers/insert-data.ts @@ -1,91 +1,105 @@ import { MongoClient } from 'mongodb'; import type { Db, MongoServerError } from 'mongodb'; -const CONNECTION_URI = 'mongodb://127.0.0.1:27091'; - -export async function dropDatabase(db: Db | string) { - const database = typeof db === 'string' ? client.db(db) : db; - try { - await database.dropDatabase(); - } catch (err) { - const codeName = (err as MongoServerError).codeName; - if (codeName !== 'NamespaceNotFound') { - throw err; - } - } +import { + DEFAULT_CONNECTION_STRING_1, + DEFAULT_CONNECTION_STRING_2, + TEST_MULTIPLE_CONNECTIONS, +} from './compass'; + +// This is a list of all the known database names that get created by tests so +// that we can know what to drop when we clean up before every test. If a new +// test starts to create another database, add it here so that it will also be +// cleaned up. +const dbNames = [ + 'test', + 'my-sidebar-database', + 'my-instance-database', + 'fle-test', + 'db-for-fle', +]; + +// These get added to the 'test' database for each client in +// createDummyCollections(). It is sometimes handy to have an empty collection +// already created so that the test can operate on it without first having to +// create it itself. +const testCollectionNames = [ + 'json-array', + 'json-file', + 'extended-json-file', + 'csv-file', + 'array-documents', + 'bom-csv-file', + 'latin1', + 'broken-delimiter', + 'numbers', + 'import-stop-first-error', + 'import-with-errors', + 'compass-import-abort-e2e-test', +]; + +// lots of collections to test virtual scrolling +for (let i = 0; i < 26; ++i) { + testCollectionNames.push('zzz' + String.fromCharCode('a'.charCodeAt(0) + i)); } -export async function createBlankCollection(db: Db | string, name: string) { - const database = typeof db === 'string' ? client.db(db) : db; - try { - await database.createCollection(name); - } catch (err) { - const codeName = (err as MongoServerError).codeName; - if (codeName === 'NamespaceExists') { - await database.collection(name).deleteMany({}); - } else { - throw err; - } +let clients: MongoClient[]; +let test_dbs: Db[]; + +before(async () => { + // Insert data on both connections so that the same databases and collections + // will exist on both servers and then anything that's not properly scoped to + // the correct connection has a chance to operate on the wrong one and + // hopefully fail a test. + // This should also mean that the database or collection name that we try and + // use is always ambiguous, so we're forced to deal with it early in tests. + const connectionStrings = [DEFAULT_CONNECTION_STRING_1]; + if (TEST_MULTIPLE_CONNECTIONS) { + connectionStrings.push(DEFAULT_CONNECTION_STRING_2); } -} + clients = connectionStrings.map( + (connectionString) => new MongoClient(connectionString) + ); -let client: MongoClient; + await Promise.all(clients.map((client) => client.connect())); -before(async () => { - client = new MongoClient(CONNECTION_URI); - await client.connect(); - console.log(`Connected successfully to ${CONNECTION_URI} for inserting data`); + console.log( + `Connected successfully to ${connectionStrings.join( + ' and ' + )} for inserting data` + ); + + test_dbs = clients.map((client) => client.db('test')); }); after(async () => { - await client.close(); + await Promise.all(clients.map((client) => client.close())); }); beforeEach(async () => { - // Drop the databases that get created by tests just in case tests failed to - // clean them up. - await Promise.all( - [ - 'test', - 'my-sidebar-database', - 'my-instance-database', - 'fle-test', - 'db-for-fle', - 'multiple-collections', - ].map((db) => dropDatabase(client.db(db))) - ); + // Drop the databases that get created by tests or the functions below + const promises = []; + + for (const client of clients) { + for (const dbName of dbNames) { + promises.push(_dropDatabase(client.db(dbName))); + } + } + + await Promise.all(promises); }); export async function createDummyCollections(): Promise { - const db = client.db('test'); const promises = []; - // Create some empty collections for the import tests so each one won't have - // to possibly drop and create via the UI every time. - // (named loosely after the relevant test) - promises.push(createBlankCollection(db, 'json-array')); - promises.push(createBlankCollection(db, 'json-file')); - promises.push(createBlankCollection(db, 'extended-json-file')); - promises.push(createBlankCollection(db, 'csv-file')); - promises.push(createBlankCollection(db, 'array-documents')); - promises.push(createBlankCollection(db, 'bom-csv-file')); - promises.push(createBlankCollection(db, 'latin1')); - promises.push(createBlankCollection(db, 'broken-delimiter')); - promises.push(createBlankCollection(db, 'numbers')); - promises.push(createBlankCollection(db, 'import-stop-first-error')); - promises.push(createBlankCollection(db, 'import-with-errors')); - promises.push(createBlankCollection(db, 'compass-import-abort-e2e-test')); - - // lots of collections to test virtual scrolling - for (let i = 0; i < 26; ++i) { - promises.push( - createBlankCollection( - db, - 'zzz' + String.fromCharCode('a'.charCodeAt(0) + i) - ) - ); + for (const db of test_dbs) { + // Create some empty collections for the import tests so each one won't have + // to possibly drop and create via the UI every time. + // (named loosely after the relevant test) + for (const collectionName of testCollectionNames) { + promises.push(_createBlankCollection(db, collectionName)); + } } - await Promise.all(promises); } @@ -93,25 +107,28 @@ export async function createNestedDocumentsCollection( name = 'nestedDocs', numberOfRecords = 1000 ): Promise { - const db = client.db('test'); - await db.collection(name).insertMany( - [...Array(numberOfRecords).keys()].map((i) => ({ - names: { - firstName: `${i}-firstName`, - lastName: `${i}-lastName`, - }, - addresses: [`${i}-address1`, `${i}-address2`], - phoneNumbers: [ - { - label: `${i}-home`, - number: `${i}-12345`, - }, - { - label: `${i}-work`, - number: `${i}-6789`, - }, - ], - })) + await Promise.all( + test_dbs.map(async (db) => { + await db.collection(name).insertMany( + [...Array(numberOfRecords).keys()].map((i) => ({ + names: { + firstName: `${i}-firstName`, + lastName: `${i}-lastName`, + }, + addresses: [`${i}-address1`, `${i}-address2`], + phoneNumbers: [ + { + label: `${i}-home`, + number: `${i}-12345`, + }, + { + label: `${i}-work`, + number: `${i}-6789`, + }, + ], + })) + ); + }) ); } @@ -119,11 +136,15 @@ export async function createNumbersCollection( name = 'numbers', numberOfRecords = 1000 ): Promise { - const db = client.db('test'); - - await db - .collection(name) - .insertMany([...Array(numberOfRecords).keys()].map((i) => ({ i, j: 0 }))); + await Promise.all( + test_dbs.map(async (db) => { + await db + .collection(name) + .insertMany( + [...Array(numberOfRecords).keys()].map((i) => ({ i, j: 0 })) + ); + }) + ); } // Useful for testing collation with `numericOrdering`. @@ -131,38 +152,66 @@ export async function createNumbersStringCollection( name = 'numbers-strings', numberOfRecords = 10 ): Promise { - const db = client.db('test'); - - await db.collection(name).insertMany( - [...Array(numberOfRecords).keys()].map((i) => ({ - i, - iString: `${i * 20}`, - j: 0, - })) + await Promise.all( + test_dbs.map(async (db) => { + await db.collection(name).insertMany( + [...Array(numberOfRecords).keys()].map((i) => ({ + i, + iString: `${i * 20}`, + j: 0, + })) + ); + }) ); } -export async function createMultipleCollections(): Promise { - const db = client.db('multiple-collections'); - - await db - .collection('one') - .insertMany([...Array(10).keys()].map((i) => ({ i, j: 0 }))); - - await db - .collection('two') - .insertMany([...Array(10).keys()].map((i) => ({ i, j: 0 }))); -} - export async function createGeospatialCollection(): Promise { - const db = client.db('test'); + await Promise.all( + test_dbs.map(async (db) => { + const lon = () => Math.random() * 360 - 180; + const lat = () => Math.random() * 180 - 90; + + await db.collection('geospatial').insertMany( + [...Array(1000).keys()].map(() => ({ + location: { type: 'Point', coordinates: [lon(), lat()] }, + })) + ); + }) + ); +} - const lon = () => Math.random() * 360 - 180; - const lat = () => Math.random() * 180 - 90; +// WARNING: please don't export _dropDatabase because this file is written to +// manage ALL test databases and collections and clean them up. If we start +// creating arbitrary databases and collections in tests then those tests have +// to start managing arbitrary cleanup too, defeating the purpose. +// Anything you put in the beforeEach hook above will be dropped automatically. +async function _dropDatabase(db: Db) { + try { + await db.dropDatabase(); + } catch (err) { + const codeName = (err as MongoServerError).codeName; + if (codeName !== 'NamespaceNotFound') { + throw err; + } + } +} - await db.collection('geospatial').insertMany( - [...Array(1000).keys()].map(() => ({ - location: { type: 'Point', coordinates: [lon(), lat()] }, - })) - ); +// WARNING: please don't export _createBlankCollection because this file is +// written to manage ALL test dataases and collections and clean them up. If we +// start creating arbitrary databases and collections in tests then those tests +// have to start managing arbitrary cleanup too, defeating the purpose. +// Just add more to createDummyCollections(), but really anything you put inside +// one of the databases that get dropped in the beforeEach hook above will be +// dropped automatically. +async function _createBlankCollection(db: Db, name: string) { + try { + await db.createCollection(name); + } catch (err) { + const codeName = (err as MongoServerError).codeName; + if (codeName === 'NamespaceExists') { + await db.collection(name).deleteMany({}); + } else { + throw err; + } + } } diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 5d8db43f6b0..a0b4f84857c 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -21,6 +21,10 @@ export const SettingsInputElement = (settingName: string): string => { return `${SettingsModal} [data-testid="${settingName}"]`; }; +// LG Modals +export const LGModal = '[data-testid="lg-modal"]'; +export const LGModalClose = '[data-testid="lg-modal-close_button"]'; + // LG Toasts container (these test ids are used by LG in the toast and are not in the code anywhere). export const LGToastContainer = '[data-testid="lg-toast-scroll-container"]'; export const LGToastCloseButton = '[data-testid="lg-toast-dismiss-button"]'; @@ -303,6 +307,20 @@ export const Multiple = { '[data-testid="connections-list-title-actions-import-saved-connections-action"]', InUseEncryptionMarker: '[data-action="open-csfle-modal"]', + + ConnectedConnectionItems: + '[role="treeitem"][aria-level="1"] [data-is-connected=true]', + + connectionItemByName: ( + connectionName: string, + { connected }: { connected?: boolean } = {} + ) => { + const connectedFilter = + connected !== undefined + ? `[data-is-connected="${connected.toString()}"]` + : ''; + return `[role="treeitem"][aria-level="1"] [data-connection-name="${connectionName}"]${connectedFilter}`; + }, }; // Rename Collection Modal @@ -332,6 +350,8 @@ export const RenameCollectionButton = export const DropDatabaseButton = '[data-action="drop-database"]'; export const CreateCollectionButton = '[data-action="create-collection"]'; export const DatabaseCollectionPlaceholder = '[data-testid="placeholder"]'; +export const CollapseConnectionsButton = + '[data-testid="connections-list-title-actions-collapse-all-connections-action"]'; export const sidebarDatabase = ( // TODO(COMPASS-7906): don't allow undefined connectionId diff --git a/packages/compass-e2e-tests/index.ts b/packages/compass-e2e-tests/index.ts index ec78d2f5578..484ebece2ae 100644 --- a/packages/compass-e2e-tests/index.ts +++ b/packages/compass-e2e-tests/index.ts @@ -17,7 +17,6 @@ import { COMPASS_PATH, LOG_PATH, removeUserDataDir, - MONGODB_TEST_SERVER_PORT, updateMongoDBServerInfo, } from './helpers/compass'; import ResultLogger from './helpers/result-logger'; @@ -60,7 +59,7 @@ async function setup() { // When working on the tests it is faster to just keep the server running. if (!disableStartStop) { debug('Starting MongoDB server'); - crossSpawn.sync('npm', ['run', 'start-server'], { stdio: 'inherit' }); + crossSpawn.sync('npm', ['run', 'start-servers'], { stdio: 'inherit' }); if (shouldTestCompassWeb) { debug('Starting Compass Web'); @@ -143,23 +142,13 @@ function cleanup() { debug('Stopping MongoDB server'); try { - crossSpawn.sync( - 'npm', - [ - 'run', - 'stop-server', - '--', - '--port', - String(MONGODB_TEST_SERVER_PORT), - ], - { - // If it's taking too long we might as well kill the process and move on, - // mongodb-runner is flaky sometimes and in ci `posttest-ci` script will - // take care of additional clean up anyway - timeout: 120_000, - stdio: 'inherit', - } - ); + crossSpawn.sync('npm', ['run', 'stop-servers'], { + // If it's taking too long we might as well kill the process and move on, + // mongodb-runner is flaky sometimes and in ci `posttest-ci` script will + // take care of additional clean up anyway + timeout: 120_000, + stdio: 'inherit', + }); } catch (e) { debug('Failed to stop MongoDB Server', e); } diff --git a/packages/compass-e2e-tests/package.json b/packages/compass-e2e-tests/package.json index f2005c1aa8c..a0dc7d16fef 100644 --- a/packages/compass-e2e-tests/package.json +++ b/packages/compass-e2e-tests/package.json @@ -17,8 +17,12 @@ "test-packaged": "npm run test -- -- --test-packaged-app", "test-packaged-ci": "npm run test-ci -- -- --test-packaged-app", "reformat": "npm run eslint . -- --fix && npm run prettier -- --write .", - "start-server": "mongodb-runner start --id=e2e --topology=replset --secondaries=0 -- --port 27091", - "stop-server": "mongodb-runner stop --id=e2e", + "start-server-1": "mongodb-runner start --id=e2e-1 --topology=replset --secondaries=0 -- --port 27091", + "stop-server-1": "mongodb-runner stop --id=e2e-1", + "start-server-2": "mongodb-runner start --id=e2e-2 --topology=replset --secondaries=0 -- --port 27092", + "stop-server-2": "mongodb-runner stop --id=e2e-2", + "start-servers": "npm run start-server-1 && npm run start-server-2", + "stop-servers": "npm run stop-server-1 && npm run stop-server-2", "unzip-fixtures": "ts-node ./scripts/gunzip.ts fixtures/*.gz", "test-noserver": "env DEBUG=hadron*,mongo*,compass*,xvfb-maybe* npm run test -- --disable-start-stop --bail", "test-noserver-nocompile": "env DEBUG=hadron*,mongo*,compass*,xvfb-maybe* npm run test -- --no-native-modules --no-compile --disable-start-stop --bail", diff --git a/packages/compass-e2e-tests/tests/atlas-login.test.ts b/packages/compass-e2e-tests/tests/atlas-login.test.ts index c1128bd76b7..4264952a558 100644 --- a/packages/compass-e2e-tests/tests/atlas-login.test.ts +++ b/packages/compass-e2e-tests/tests/atlas-login.test.ts @@ -6,7 +6,7 @@ import { Selectors, skipForWeb, TEST_COMPASS_WEB, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import type { OIDCMockProviderConfig } from '@mongodb-js/oidc-mock-provider'; @@ -124,6 +124,7 @@ describe('Atlas Login', function () { 'browserCommandForOIDCAuth', getTestBrowserShellCommand() ); + await browser.setupDefaultConnections(); }); afterEach(async function () { @@ -273,9 +274,10 @@ describe('Atlas Login', function () { describe('in CRUD view', function () { beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' @@ -305,9 +307,10 @@ describe('Atlas Login', function () { describe('in Aggregation Builder view', function () { beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Aggregations' diff --git a/packages/compass-e2e-tests/tests/auto-connect.test.ts b/packages/compass-e2e-tests/tests/auto-connect.test.ts index 6cbe7bba03a..bb02397f2ce 100644 --- a/packages/compass-e2e-tests/tests/auto-connect.test.ts +++ b/packages/compass-e2e-tests/tests/auto-connect.test.ts @@ -15,10 +15,15 @@ import path from 'path'; import { promises as fs } from 'fs'; const connectionStringSuccess = 'mongodb://127.0.0.1:27091/test'; +const connectionNameSuccess = connectionNameFromString(connectionStringSuccess); const connectionStringSuccessTitle = '127.0.0.1:27091'; const connectionStringUnreachable = 'mongodb://127.0.0.1:27091/test?tls=true&serverSelectionTimeoutMS=10'; +const connectionNameUnreachable = connectionNameFromString( + connectionStringUnreachable +); const connectionStringInvalid = 'http://example.com'; +const connectionNameInvalid = connectionNameFromString(connectionStringInvalid); describe('Automatically connecting from the command line', function () { let tmpdir: string; @@ -26,7 +31,7 @@ describe('Automatically connecting from the command line', function () { before(function () { skipForWeb(this, 'cli parameters not supported in compass-web'); - // TODO(COMPASS-8010): pory these tests + // TODO(COMPASS-8010): port these tests if (TEST_MULTIPLE_CONNECTIONS) { this.skip(); } @@ -88,13 +93,16 @@ describe('Automatically connecting from the command line', function () { compass: Compass, expectedTitle = connectionStringSuccessTitle ) { - await compass.browser.waitForConnectionResult('success'); + const connectionName = connectionNameFromString(connectionStringSuccess); + await compass.browser.waitForConnectionResult(connectionName, { + connectionStatus: 'success', + }); const sidebarTitle = await compass.browser .$(Selectors.SidebarTitle) .getText(); expect(sidebarTitle).to.eq(expectedTitle); const result = await compass.browser.shellEval( - connectionNameFromString(connectionStringSuccess), + connectionName, 'db.runCommand({ connectionStatus: 1 })', true ); @@ -141,7 +149,10 @@ describe('Automatically connecting from the command line', function () { wrapBinary: positionalArgs(args), }); try { - const error = await compass.browser.waitForConnectionResult('failure'); + const error = await compass.browser.waitForConnectionResult( + connectionNameUnreachable, + { connectionStatus: 'failure' } + ); expect(error).to.match( /ECONNRESET|Server selection timed out|Client network socket disconnected/i ); @@ -163,7 +174,10 @@ describe('Automatically connecting from the command line', function () { }); const { browser } = compass; try { - const error = await browser.waitForConnectionResult('failure'); + const error = await browser.waitForConnectionResult( + connectionNameSuccess, + { connectionStatus: 'failure' } + ); expect(error).to.include('Authentication failed'); const connectFormState = await browser.getConnectFormState(); expect(connectFormState.defaultUsername).to.equal('doesnotexist'); @@ -185,7 +199,10 @@ describe('Automatically connecting from the command line', function () { wrapBinary: positionalArgs(args), }); try { - const error = await compass.browser.waitForConnectionResult('failure'); + const error = await compass.browser.waitForConnectionResult( + connectionNameInvalid, + { connectionStatus: 'failure' } + ); expect(error).to.include('Invalid scheme'); } finally { await cleanup(compass); @@ -198,8 +215,13 @@ describe('Automatically connecting from the command line', function () { `--file=${path.join(tmpdir, 'doesnotexist.json')}`, ]), }); + const connectionName = + 'no connection can appear because the file is invalid'; try { - const error = await compass.browser.waitForConnectionResult('failure'); + const error = await compass.browser.waitForConnectionResult( + connectionName, + { connectionStatus: 'failure' } + ); expect(error).to.include('ENOENT'); } finally { await cleanup(compass); @@ -213,11 +235,15 @@ describe('Automatically connecting from the command line', function () { }); try { const { browser } = compass; - await browser.waitForConnectionResult('success'); + await browser.waitForConnectionResult(connectionNameSuccess, { + connectionStatus: 'success', + }); await browser.execute(() => { location.reload(); }); - await browser.waitForConnectionResult('success'); + await browser.waitForConnectionResult(connectionNameSuccess, { + connectionStatus: 'success', + }); await browser.disconnectAll(); await browser.execute(() => { location.reload(); @@ -242,7 +268,9 @@ describe('Automatically connecting from the command line', function () { }); try { const { browser } = compass; - await browser.waitForConnectionResult('success'); + await browser.waitForConnectionResult(connectionNameSuccess, { + connectionStatus: 'success', + }); await browser.execute(() => { // eslint-disable-next-line @typescript-eslint/no-var-requires require('electron').ipcRenderer.call('test:show-connect-window'); @@ -275,7 +303,9 @@ describe('Automatically connecting from the command line', function () { }); try { const browser = compass.browser; - await browser.waitForConnectionResult('success'); + await browser.waitForConnectionResult(connectionNameSuccess, { + connectionStatus: 'success', + }); await browser.disconnectAll(); // this is not the ideal check because by default the recent connections diff --git a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts index 4c4b8b09961..253aad23f80 100644 --- a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts @@ -8,7 +8,7 @@ import { outputFilename, serverSatisfies, skipForWeb, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -127,12 +127,14 @@ describe('Collection aggregations tab', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); await createNestedDocumentsCollection('nestedDocs', 10); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); // set guide cue to not show up await browser.execute((key) => { localStorage.setItem(key, 'true'); @@ -140,7 +142,7 @@ describe('Collection aggregations tab', function () { // Some tests navigate away from the numbers collection aggregations tab await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Aggregations' @@ -423,8 +425,6 @@ describe('Collection aggregations tab', function () { 'my-view-from-pipeline' ); - await browser.screenshot('create-view-modal.png'); - // click create button const createButton = await browser .$(Selectors.CreateViewModal) @@ -437,7 +437,7 @@ describe('Collection aggregations tab', function () { // choose Duplicate view await browser.selectCollectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'my-view-from-pipeline', 'duplicate-view' @@ -455,8 +455,6 @@ describe('Collection aggregations tab', function () { ); await confirmDuplicateButton.waitForEnabled(); - await browser.screenshot('duplicate-view-modal.png'); - await confirmDuplicateButton.click(); await duplicateModal.waitForDisplayed({ reverse: true }); @@ -465,7 +463,7 @@ describe('Collection aggregations tab', function () { // now select modify view of the non-duplicate await browser.selectCollectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'my-view-from-pipeline', 'modify-view' @@ -957,8 +955,6 @@ describe('Collection aggregations tab', function () { const exportModal = await browser.$(Selectors.ExportModal); await exportModal.waitForDisplayed(); - await browser.screenshot('export-modal.png'); - // Make sure the aggregation is shown in the modal. const exportModalAggregationTextElement = await browser.$( Selectors.ExportModalCodePreview @@ -1010,8 +1006,6 @@ describe('Collection aggregations tab', function () { expect(await modal.getText()).to.contain('Query Performance Summary'); - await browser.screenshot('aggregation-explain-modal.png'); - await browser.clickVisible(Selectors.AggregationExplainModalCloseButton); await modal.waitForDisplayed({ reverse: true }); }); @@ -1602,7 +1596,7 @@ describe('Collection aggregations tab', function () { describe('expanding and collapsing of documents', function () { beforeEach(async function () { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'nestedDocs', 'Aggregations' diff --git a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts index cfd3bc70036..e7cb589de9c 100644 --- a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts +++ b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts @@ -9,7 +9,7 @@ import { screenshotIfFailed, skipForWeb, TEST_COMPASS_WEB, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -52,13 +52,15 @@ describe('Collection ai query', function () { telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' diff --git a/packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts b/packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts index 3d2633ccb97..e1a1934a635 100644 --- a/packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts +++ b/packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts @@ -6,7 +6,7 @@ import { init, cleanup, screenshotIfFailed, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -21,6 +21,7 @@ describe('Bulk Delete', function () { telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); after(async function () { @@ -30,9 +31,10 @@ describe('Bulk Delete', function () { beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' diff --git a/packages/compass-e2e-tests/tests/collection-bulk-update.test.ts b/packages/compass-e2e-tests/tests/collection-bulk-update.test.ts index 583c98b7fd9..ae3351d8d82 100644 --- a/packages/compass-e2e-tests/tests/collection-bulk-update.test.ts +++ b/packages/compass-e2e-tests/tests/collection-bulk-update.test.ts @@ -7,7 +7,7 @@ import { cleanup, screenshotIfFailed, skipForWeb, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -22,6 +22,7 @@ describe('Bulk Update', () => { telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); after(async function () { @@ -31,9 +32,10 @@ describe('Bulk Update', () => { beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' diff --git a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts index 4901c108f92..b590acb7a0d 100644 --- a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts @@ -9,7 +9,7 @@ import { screenshotIfFailed, TEST_COMPASS_WEB, skipForWeb, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -117,14 +117,16 @@ describe('Collection documents tab', function () { telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); await createNestedDocumentsCollection('nestedDocs', 10); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' @@ -637,7 +639,7 @@ FindIterable result = collection.find(filter);`); describe('expanding and collapsing of documents', function () { beforeEach(async function () { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'nestedDocs', 'Documents' diff --git a/packages/compass-e2e-tests/tests/collection-export.test.ts b/packages/compass-e2e-tests/tests/collection-export.test.ts index f557dde6383..9d32db95f68 100644 --- a/packages/compass-e2e-tests/tests/collection-export.test.ts +++ b/packages/compass-e2e-tests/tests/collection-export.test.ts @@ -10,7 +10,7 @@ import { outputFilename, skipForWeb, TEST_COMPASS_WEB, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -49,6 +49,7 @@ describe('Collection export', function () { telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); after(async function () { @@ -67,9 +68,10 @@ describe('Collection export', function () { describe('with the numbers collection', function () { beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' @@ -883,9 +885,10 @@ describe('Collection export', function () { describe('with the number-strings collection', function () { beforeEach(async function () { await createNumbersStringCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers-strings', 'Documents' diff --git a/packages/compass-e2e-tests/tests/collection-heading.test.ts b/packages/compass-e2e-tests/tests/collection-heading.test.ts index 0da2ad865be..75ab21f9a92 100644 --- a/packages/compass-e2e-tests/tests/collection-heading.test.ts +++ b/packages/compass-e2e-tests/tests/collection-heading.test.ts @@ -4,7 +4,7 @@ import { init, cleanup, screenshotIfFailed, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -17,13 +17,15 @@ describe('Collection heading', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' diff --git a/packages/compass-e2e-tests/tests/collection-import.test.ts b/packages/compass-e2e-tests/tests/collection-import.test.ts index b3b116aa615..5ed7bef87be 100644 --- a/packages/compass-e2e-tests/tests/collection-import.test.ts +++ b/packages/compass-e2e-tests/tests/collection-import.test.ts @@ -9,7 +9,7 @@ import { screenshotIfFailed, skipForWeb, TEST_COMPASS_WEB, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import { getFirstListDocument } from '../helpers/read-first-document-content'; import type { Compass } from '../helpers/compass'; @@ -104,12 +104,14 @@ describe('Collection import', function () { telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); await createDummyCollections(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); }); after(async function () { @@ -127,7 +129,7 @@ describe('Collection import', function () { it('supports single JSON objects', async function () { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'json-array', 'Documents' @@ -208,7 +210,7 @@ describe('Collection import', function () { it('supports single objects in document view mode', async function () { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'json-array', 'Documents' @@ -284,7 +286,7 @@ describe('Collection import', function () { it('supports JSON arrays', async function () { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'json-array', 'Documents' @@ -345,7 +347,7 @@ describe('Collection import', function () { it('displays an error for a malformed JSON array', async function () { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'json-array', 'Documents' @@ -386,7 +388,7 @@ describe('Collection import', function () { const jsonPath = path.resolve(__dirname, '..', 'fixtures', 'listings.json'); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'json-file', 'Documents' @@ -436,7 +438,7 @@ describe('Collection import', function () { ); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'extended-json-file', 'Documents' @@ -487,7 +489,7 @@ describe('Collection import', function () { ); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'extended-json-file', 'Documents' @@ -530,7 +532,7 @@ describe('Collection import', function () { const csvPath = path.resolve(__dirname, '..', 'fixtures', 'listings.csv'); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'csv-file', 'Documents' @@ -661,7 +663,7 @@ describe('Collection import', function () { ); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'array-documents', 'Documents' @@ -876,7 +878,7 @@ describe('Collection import', function () { ); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'bom-csv-file', 'Documents' @@ -968,7 +970,7 @@ describe('Collection import', function () { const csvPath = path.resolve(__dirname, '..', 'fixtures', 'listings.csv'); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'csv-file', 'Documents' @@ -1027,7 +1029,7 @@ describe('Collection import', function () { ); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'broken-delimiter', 'Documents' @@ -1117,7 +1119,7 @@ describe('Collection import', function () { ); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'import-stop-first-error', 'Documents' @@ -1173,7 +1175,7 @@ describe('Collection import', function () { const jsonPath = path.resolve(__dirname, '..', 'fixtures', fileName); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'import-with-errors', 'Documents' @@ -1244,7 +1246,7 @@ describe('Collection import', function () { const csvPath = path.resolve(__dirname, '..', 'fixtures', 'listings.csv'); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'compass-import-abort-e2e-test', 'Documents' @@ -1320,7 +1322,7 @@ describe('Collection import', function () { ); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'compass-import-abort-e2e-test', 'Documents' @@ -1387,7 +1389,7 @@ describe('Collection import', function () { const csvPath = path.resolve(__dirname, '..', 'fixtures', 'listings.csv'); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'compass-import-abort-e2e-test', 'Documents' diff --git a/packages/compass-e2e-tests/tests/collection-indexes-tab.test.ts b/packages/compass-e2e-tests/tests/collection-indexes-tab.test.ts index ba815335e88..33b1578dde2 100644 --- a/packages/compass-e2e-tests/tests/collection-indexes-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-indexes-tab.test.ts @@ -6,7 +6,7 @@ import { cleanup, screenshotIfFailed, serverSatisfies, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -21,13 +21,15 @@ describe('Collection indexes tab', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Indexes' @@ -159,8 +161,6 @@ describe('Collection indexes tab', function () { 'columnstore' ); - await browser.screenshot('create-index-modal-columnstore.png'); - await browser.clickVisible(Selectors.CreateIndexConfirmButton); await createModal.waitForDisplayed({ reverse: true }); diff --git a/packages/compass-e2e-tests/tests/collection-rename.test.ts b/packages/compass-e2e-tests/tests/collection-rename.test.ts index ee4fc9c539f..47b7f820d41 100644 --- a/packages/compass-e2e-tests/tests/collection-rename.test.ts +++ b/packages/compass-e2e-tests/tests/collection-rename.test.ts @@ -6,16 +6,15 @@ import { screenshotIfFailed, skipForWeb, TEST_COMPASS_WEB, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { CompassBrowser } from '../helpers/compass-browser'; -import { createBlankCollection, dropDatabase } from '../helpers/insert-data'; +import { createDummyCollections } from '../helpers/insert-data'; import * as Selectors from '../helpers/selectors'; -const initialName = 'numbers'; -const newName = 'renamed'; - -const databaseName = 'rename-collection'; +const databaseName = 'test'; +const initialCollectionName = 'numbers'; +const newCollectionName = 'renamed'; class RenameCollectionModal { constructor(private browser: CompassBrowser) {} @@ -50,23 +49,23 @@ class RenameCollectionModal { }); } - async enterNewCollectionName(newName: string) { + async enterNewCollectionName(newCollectionName: string) { const input = await this.browser.$(Selectors.RenameCollectionModalInput); await input.clearValue(); - await input.addValue(newName); + await input.addValue(newCollectionName); } } async function renameCollectionSuccessFlow( browser: CompassBrowser, - newName: string + newCollectionName: string ) { const page = new RenameCollectionModal(browser); // wait for the collection modal to appear await page.isVisible(); // enter the new name - await page.enterNewCollectionName(newName); + await page.enterNewCollectionName(newCollectionName); // submit the form and confirm submission await page.submitButton.click(); @@ -88,18 +87,19 @@ describe('Collection Rename Modal', () => { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); await browser.setFeature('enableRenameCollectionModal', true); }); beforeEach(async function () { - await dropDatabase(databaseName); - - await createBlankCollection(databaseName, initialName); - await createBlankCollection(databaseName, 'bar'); + await createDummyCollections(); - await browser.connectWithConnectionString(); - connectionId = await browser.getConnectionIdByName(DEFAULT_CONNECTION_NAME); + await browser.disconnectAll(); + await browser.connectToDefaults(); + connectionId = await browser.getConnectionIdByName( + DEFAULT_CONNECTION_NAME_1 + ); }); after(async function () { @@ -111,7 +111,6 @@ describe('Collection Rename Modal', () => { }); afterEach(async function () { - await dropDatabase(databaseName); await screenshotIfFailed(compass, this.currentTest); }); @@ -119,9 +118,9 @@ describe('Collection Rename Modal', () => { it('collection rename shows up on collection view', async () => { // open a collection tab await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, databaseName, - initialName + initialCollectionName ); const headerSelector = Selectors.CollectionHeader; @@ -129,18 +128,28 @@ describe('Collection Rename Modal', () => { await browser.$(headerSelector).waitForDisplayed(); await browser.selectCollectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, databaseName, - initialName, + initialCollectionName, 'rename-collection' ); - await renameCollectionSuccessFlow(browser, newName); + await renameCollectionSuccessFlow(browser, newCollectionName); // confirm that the new collection name is shown in the sidebar - await browser.setValueVisible(Selectors.SidebarFilterInput, ''); + await browser.clickVisible(Selectors.SidebarFilterInput); + await browser.setValueVisible( + Selectors.SidebarFilterInput, + `^(${databaseName}|${newCollectionName})$` + ); await browser - .$(Selectors.sidebarCollection(connectionId, databaseName, newName)) + .$( + Selectors.sidebarCollection( + connectionId, + databaseName, + newCollectionName + ) + ) .waitForDisplayed(); // confirm that the header in the workspace changes @@ -150,17 +159,17 @@ describe('Collection Rename Modal', () => { .$(headerSelector) .getText(); return ( - collectionHeaderContent.includes(newName) && - !collectionHeaderContent.includes(initialName) + collectionHeaderContent.includes(newCollectionName) && + !collectionHeaderContent.includes(initialCollectionName) ); }); }); it('collection rename can be retried after an error renaming the collection', async () => { await browser.selectCollectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, databaseName, - initialName, + initialCollectionName, 'rename-collection' ); @@ -180,7 +189,7 @@ describe('Collection Rename Modal', () => { await modal.errorBanner.waitForDisplayed(); // try again, expecting success - await modal.enterNewCollectionName(newName); + await modal.enterNewCollectionName(newCollectionName); await modal.submitButton.click(); await modal.confirmationScreen.waitForDisplayed(); await modal.submitButton.click(); @@ -193,9 +202,9 @@ describe('Collection Rename Modal', () => { describe('modal dismiss', () => { it('clears modal state when dismissed', async () => { await browser.selectCollectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, databaseName, - initialName, + initialCollectionName, 'rename-collection' ); @@ -210,14 +219,16 @@ describe('Collection Rename Modal', () => { // re-open the modal await browser.selectCollectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, databaseName, - initialName, + initialCollectionName, 'rename-collection' ); // assert that the form state has reset - expect(await modal.collectionNameInput.getValue()).to.equal(initialName); + expect(await modal.collectionNameInput.getValue()).to.equal( + initialCollectionName + ); }); }); }); diff --git a/packages/compass-e2e-tests/tests/collection-schema-tab.test.ts b/packages/compass-e2e-tests/tests/collection-schema-tab.test.ts index fe9d291edc9..74aa0c60868 100644 --- a/packages/compass-e2e-tests/tests/collection-schema-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-schema-tab.test.ts @@ -5,7 +5,7 @@ import { cleanup, screenshotIfFailed, skipForWeb, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -23,12 +23,14 @@ describe('Collection schema tab', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); await createGeospatialCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); }); after(async function () { @@ -41,7 +43,7 @@ describe('Collection schema tab', function () { it('analyzes a schema', async function () { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Schema' @@ -77,7 +79,7 @@ describe('Collection schema tab', function () { await browser.setFeature('enableMaps', enableMaps); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'geospatial', 'Schema' diff --git a/packages/compass-e2e-tests/tests/collection-validation-tab.test.ts b/packages/compass-e2e-tests/tests/collection-validation-tab.test.ts index f3ac21a0d1b..e94905a5d48 100644 --- a/packages/compass-e2e-tests/tests/collection-validation-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-validation-tab.test.ts @@ -3,7 +3,7 @@ import { init, cleanup, screenshotIfFailed, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -22,13 +22,15 @@ describe('Collection validation tab', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Validation' diff --git a/packages/compass-e2e-tests/tests/connection-form.test.ts b/packages/compass-e2e-tests/tests/connection-form.test.ts index 4fde54353fe..2c8d6e16c51 100644 --- a/packages/compass-e2e-tests/tests/connection-form.test.ts +++ b/packages/compass-e2e-tests/tests/connection-form.test.ts @@ -107,6 +107,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect( await browser.$(Selectors.ConnectionFormStringInput).getValue() @@ -146,6 +147,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect( await browser.$(Selectors.ConnectionFormStringInput).getValue() @@ -185,6 +187,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect( await browser.$(Selectors.ConnectionFormStringInput).getValue() @@ -230,6 +233,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(true); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect(await browser.getConnectFormConnectionString(true)).to.equal( connectionString @@ -283,6 +287,7 @@ describe('Connection form', function () { expectedState.tlsCAFile = tlsCAFile; expectedState.tlsCertificateKeyFile = tlsCertificateKeyFile; + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect(await browser.getConnectFormConnectionString(true)).to.equal( connectionString @@ -348,6 +353,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect( await browser.$(Selectors.ConnectionFormStringInput).getValue() @@ -390,6 +396,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(true); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect(await browser.getConnectFormConnectionString(true)).to.equal( connectionString @@ -434,6 +441,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(true); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect(await browser.getConnectFormConnectionString(true)).to.equal( connectionString @@ -480,6 +488,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(true); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect(await browser.getConnectFormConnectionString(true)).to.equal( connectionString @@ -527,6 +536,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect( await browser.$(Selectors.ConnectionFormStringInput).getValue() @@ -666,6 +676,7 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(false); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect(await browser.getConnectFormConnectionString(false)).to.equal( redactedConnectionString @@ -747,8 +758,6 @@ describe('Connection form', function () { const confirmModal = await browser.$(Selectors.ConfirmationModal); await confirmModal.waitForDisplayed(); - await browser.screenshot('edit-uri-confirmation-modal.png'); - await browser.clickVisible(Selectors.confirmationModalConfirmButton()); await confirmModal.waitForDisplayed({ reverse: true }); @@ -793,6 +802,9 @@ describe('Connection form', function () { const state = await browser.getConnectFormState(true); expect(state).to.deep.equal(expectedState); + delete expectedState.connectionString; + + delete expectedState.connectionString; await browser.setConnectFormState(expectedState); expect(await browser.getConnectFormConnectionString(true)).to.equal( `${connectionString}&authSource=%24external` @@ -867,8 +879,6 @@ describe('Connection form', function () { await browser.$(Selectors.FavoriteSaveButton).waitForEnabled(); - await browser.screenshot('save-favorite-modal-new.png'); - await browser.clickVisible(Selectors.FavoriteSaveButton); await browser.$(Selectors.FavoriteModal).waitForExist({ reverse: true }); diff --git a/packages/compass-e2e-tests/tests/connection.test.ts b/packages/compass-e2e-tests/tests/connection.test.ts index 3633d9421bb..0edd492a684 100644 --- a/packages/compass-e2e-tests/tests/connection.test.ts +++ b/packages/compass-e2e-tests/tests/connection.test.ts @@ -15,7 +15,7 @@ import { TEST_COMPASS_WEB, TEST_MULTIPLE_CONNECTIONS, connectionNameFromString, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, MONGODB_TEST_SERVER_PORT, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; @@ -237,8 +237,6 @@ async function assertCannotCreateDb( `not authorized on ${dbName} to execute command` ); - await browser.screenshot('create-database-modal-error.png'); - // cancel and wait for the modal to go away await browser.clickVisible(Selectors.CreateDatabaseCancelButton); await createModalElement.waitForDisplayed({ reverse: true }); @@ -278,8 +276,6 @@ async function assertCannotCreateCollection( `not authorized on ${dbName} to execute command` ); - await browser.screenshot('create-collection-modal-error.png'); - // cancel and wait for the modal to go away await browser.clickVisible(Selectors.CreateCollectionCancelButton); await createModalElement.waitForDisplayed({ reverse: true }); @@ -297,6 +293,10 @@ describe('Connection string', function () { browser = compass.browser; }); + beforeEach(async function () { + await browser.disconnectAll(); + }); + after(function () { return cleanup(compass); }); @@ -310,7 +310,7 @@ describe('Connection string', function () { await browser.connectWithConnectionString(); if (!TEST_COMPASS_WEB) { const result = await browser.shellEval( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'db.runCommand({ connectionStatus: 1 })', true ); @@ -324,7 +324,7 @@ describe('Connection string', function () { await browser.connectWithConnectionString( `mongodb://a:b@127.0.0.1:${MONGODB_TEST_SERVER_PORT}/test`, - 'failure' + { connectionStatus: 'failure' } ); if (TEST_MULTIPLE_CONNECTIONS) { const toastTitle = await browser.$(Selectors.LGToastTitle).getText(); @@ -342,6 +342,16 @@ describe('Connection string', function () { .getText(); expect(errorMessage).to.equal('Authentication failed.'); } + + // for multiple connections click the review button in the toast + if (TEST_MULTIPLE_CONNECTIONS) { + await browser.clickVisible(Selectors.ConnectionToastErrorReviewButton); + await browser.$(Selectors.ConnectionModal).waitForDisplayed(); + const errorText = await browser + .$(Selectors.ConnectionFormErrorMessage) + .getText(); + expect(errorText).to.equal('Authentication failed.'); + } }); it('can connect to an Atlas replicaset without srv', async function () { @@ -388,7 +398,6 @@ describe('Connection string', function () { await browser.connectWithConnectionString(connectionString); if (!TEST_COMPASS_WEB) { - await browser.screenshot('direct-connection-shell.png'); const result = await browser.shellEval( connectionNameFromString(connectionString), 'db.runCommand({ connectionStatus: 1 })', @@ -658,6 +667,10 @@ describe('Connection form', function () { browser = compass.browser; }); + beforeEach(async function () { + await browser.disconnectAll(); + }); + after(function () { if (TEST_COMPASS_WEB) { return; @@ -701,7 +714,6 @@ describe('Connection form', function () { ...atlasConnectionOptions, connectionName, }); - await browser.screenshot('SCDAM-SHA1-shell.png'); const result = await browser.shellEval( connectionName, 'db.runCommand({ connectionStatus: 1 })', @@ -768,7 +780,6 @@ describe('Connection form', function () { ...atlasConnectionOptions, connectionName, }); - await browser.screenshot('without-session-token-shell.png'); const result = await browser.shellEval( connectionName, 'db.runCommand({ connectionStatus: 1 })', @@ -805,7 +816,6 @@ describe('Connection form', function () { ...atlasConnectionOptions, connectionName, }); - await browser.screenshot('including-session-token-shell.png'); const result = await browser.shellEval( connectionName, 'db.runCommand({ connectionStatus: 1 })', @@ -836,8 +846,6 @@ describe('Connection form', function () { connectionName, }); - await browser.screenshot('tlsUseSystemCA-shell.png'); - // NB: The fact that we can use the shell is a regression test for COMPASS-5802. const result = await browser.shellEval( connectionName, @@ -954,7 +962,8 @@ describe('SRV connectivity', function () { it('resolves SRV connection string using OS DNS APIs', async function () { if (TEST_MULTIPLE_CONNECTIONS) { - // TODO(COMPAS-8009): we have to add support in custom commands for when connections fail + // TODO(COMPASS-8153): we have to add support in custom commands for when + // connections fail this.skip(); } @@ -966,7 +975,7 @@ describe('SRV connectivity', function () { // (Unless you have a server listening on port 27017) await browser.connectWithConnectionString( 'mongodb+srv://test1.test.build.10gen.cc/test?tls=false', - 'either' + { connectionStatus: 'either' } ); } finally { // make sure the browser gets closed otherwise if this fails the process wont exit @@ -1084,6 +1093,10 @@ describe('FLE2', function () { browser = compass.browser; }); + beforeEach(async function () { + await browser.disconnectAll(); + }); + afterEach(async function () { await screenshotIfFailed(compass, this.currentTest); }); diff --git a/packages/compass-e2e-tests/tests/database-collections-tab.test.ts b/packages/compass-e2e-tests/tests/database-collections-tab.test.ts index 08ad3d2bdba..bff9d103278 100644 --- a/packages/compass-e2e-tests/tests/database-collections-tab.test.ts +++ b/packages/compass-e2e-tests/tests/database-collections-tab.test.ts @@ -6,8 +6,8 @@ import { cleanup, screenshotIfFailed, serverSatisfies, - DEFAULT_CONNECTION_STRING, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_STRING_1, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -31,7 +31,6 @@ async function waitForCollectionAndBadge( // Hit refresh because depending on timing the card might appear without the // badge at first. Especially in Firefox for whatever reason. - await browser.screenshot('click-refresh.png'); await browser.clickVisible(Selectors.DatabaseRefreshCollectionButton); await browser.scrollToVirtualItem( @@ -49,6 +48,7 @@ describe('Database collections tab', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); after(async function () { @@ -58,9 +58,10 @@ describe('Database collections tab', function () { beforeEach(async function () { await createDummyCollections(); await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToDatabaseCollectionsTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test' ); }); @@ -132,7 +133,7 @@ describe('Database collections tab', function () { ); await browser.navigateToDatabaseCollectionsTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test' ); @@ -170,7 +171,7 @@ describe('Database collections tab', function () { // the app should still be on the database Collections tab because there are // other collections in this database - await browser.waitUntilActiveDatabaseTab(DEFAULT_CONNECTION_NAME, 'test'); + await browser.waitUntilActiveDatabaseTab(DEFAULT_CONNECTION_NAME_1, 'test'); }); it('can create a capped collection', async function () { @@ -190,7 +191,7 @@ describe('Database collections tab', function () { ); await browser.navigateToDatabaseCollectionsTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test' ); @@ -231,7 +232,7 @@ describe('Database collections tab', function () { ); await browser.navigateToDatabaseCollectionsTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test' ); @@ -267,7 +268,7 @@ describe('Database collections tab', function () { ); await browser.navigateToDatabaseCollectionsTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test' ); @@ -304,7 +305,7 @@ describe('Database collections tab', function () { ); await browser.navigateToDatabaseCollectionsTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test' ); @@ -339,7 +340,7 @@ describe('Database collections tab', function () { ); await browser.navigateToDatabaseCollectionsTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test' ); @@ -351,7 +352,7 @@ describe('Database collections tab', function () { ); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', collectionName, 'Indexes' @@ -370,7 +371,7 @@ describe('Database collections tab', function () { const coll = `zcoll-${Date.now()}`; // Create the collection and refresh - const mongoClient = new MongoClient(DEFAULT_CONNECTION_STRING); + const mongoClient = new MongoClient(DEFAULT_CONNECTION_STRING_1); await mongoClient.connect(); try { const database = mongoClient.db(db); @@ -379,7 +380,10 @@ describe('Database collections tab', function () { await mongoClient.close(); } - await browser.navigateToDatabaseCollectionsTab(DEFAULT_CONNECTION_NAME, db); + await browser.navigateToDatabaseCollectionsTab( + DEFAULT_CONNECTION_NAME_1, + db + ); await browser.clickVisible(Selectors.DatabaseRefreshCollectionButton); const collSelector = Selectors.collectionCard(db, coll); diff --git a/packages/compass-e2e-tests/tests/global-preferences.test.ts b/packages/compass-e2e-tests/tests/global-preferences.test.ts index 9dbf1cf78ea..edae3141b1d 100644 --- a/packages/compass-e2e-tests/tests/global-preferences.test.ts +++ b/packages/compass-e2e-tests/tests/global-preferences.test.ts @@ -82,7 +82,6 @@ describe('Global preferences', function () { expect(bannerText).to.equal(null); } } finally { - await browser.screenshot('global-preferences-cli.png'); await cleanup(compass); } }); @@ -114,7 +113,6 @@ describe('Global preferences', function () { expect(bannerText).to.equal(null); } } finally { - await browser.screenshot('global-preferences-yaml.png'); await cleanup(compass); } }); @@ -146,7 +144,6 @@ describe('Global preferences', function () { expect(bannerText).to.equal(null); } } finally { - await browser.screenshot('global-preferences-ejson.png'); await cleanup(compass); } }); @@ -167,7 +164,6 @@ describe('Global preferences', function () { 'This setting cannot be modified as it has been set in the global Compass configuration file.' ); } finally { - await browser.screenshot('global-preferences-networkTraffic-false.png'); await cleanup(compass); } }); @@ -213,7 +209,6 @@ describe('Global preferences', function () { expect(isShellSectionExisting).to.be.equal(false); } } finally { - await browser.screenshot('global-preferences-readOnly-true.png'); await cleanup(compass); } }); diff --git a/packages/compass-e2e-tests/tests/in-use-encryption.test.ts b/packages/compass-e2e-tests/tests/in-use-encryption.test.ts index 6c3ddbe7e3d..72de5426a04 100644 --- a/packages/compass-e2e-tests/tests/in-use-encryption.test.ts +++ b/packages/compass-e2e-tests/tests/in-use-encryption.test.ts @@ -150,7 +150,9 @@ describe('CSFLE / QE', function () { } // Wait for it to connect - await browser.waitForConnectionResult('success'); + await browser.waitForConnectionResult(connectionName, { + connectionStatus: 'success', + }); // extra pause to make very sure that it saved the connection before we disconnect await delay(10000); @@ -164,7 +166,6 @@ describe('CSFLE / QE', function () { // extra pause to make very sure that it loaded the connections await delay(10000); - await browser.screenshot('saved-connections-after-disconnect.png'); if (TEST_MULTIPLE_CONNECTIONS) { // in the multiple connections world, if we clicked the connection it @@ -319,6 +320,7 @@ describe('CSFLE / QE', function () { }); beforeEach(async function () { + await browser.disconnectAll(); await browser.connectWithConnectionForm({ hosts: [CONNECTION_HOSTS], fleKeyVaultNamespace: `${databaseName}.keyvault`, @@ -962,6 +964,10 @@ describe('CSFLE / QE', function () { browser = compass.browser; }); + beforeEach(async function () { + await browser.disconnectAll(); + }); + afterEach(async function () { if (compass) { await screenshotIfFailed(compass, this.currentTest); diff --git a/packages/compass-e2e-tests/tests/instance-databases-tab.test.ts b/packages/compass-e2e-tests/tests/instance-databases-tab.test.ts index 93a9844814f..e3e857e31ce 100644 --- a/packages/compass-e2e-tests/tests/instance-databases-tab.test.ts +++ b/packages/compass-e2e-tests/tests/instance-databases-tab.test.ts @@ -5,8 +5,8 @@ import { init, cleanup, screenshotIfFailed, - DEFAULT_CONNECTION_STRING, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_STRING_1, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -24,13 +24,18 @@ describe('Instance databases tab', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createDummyCollections(); await createNumbersCollection(); - await browser.connectWithConnectionString(); - await browser.navigateToConnectionTab(DEFAULT_CONNECTION_NAME, 'Databases'); + await browser.disconnectAll(); + await browser.connectToDefaults(); + await browser.navigateToConnectionTab( + DEFAULT_CONNECTION_NAME_1, + 'Databases' + ); }); after(async function () { @@ -100,7 +105,10 @@ describe('Instance databases tab', function () { 'add-database-modal-basic.png' ); - await browser.navigateToConnectionTab(DEFAULT_CONNECTION_NAME, 'Databases'); + await browser.navigateToConnectionTab( + DEFAULT_CONNECTION_NAME_1, + 'Databases' + ); const selector = Selectors.databaseCard(dbName); await browser.scrollToVirtualItem( @@ -136,7 +144,7 @@ describe('Instance databases tab', function () { // the app should stay on the instance Databases tab. await browser.waitUntilActiveConnectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'Databases' ); }); @@ -146,7 +154,10 @@ describe('Instance databases tab', function () { const dbSelector = Selectors.databaseCard(db); // Browse to the databases tab - await browser.navigateToConnectionTab(DEFAULT_CONNECTION_NAME, 'Databases'); + await browser.navigateToConnectionTab( + DEFAULT_CONNECTION_NAME_1, + 'Databases' + ); // Make sure the db card we're going to drop is in there. await browser.scrollToVirtualItem( @@ -163,7 +174,7 @@ describe('Instance databases tab', function () { }); // Drop the database using the driver - const mongoClient = new MongoClient(DEFAULT_CONNECTION_STRING); + const mongoClient = new MongoClient(DEFAULT_CONNECTION_STRING_1); await mongoClient.connect(); try { const database = mongoClient.db(db); diff --git a/packages/compass-e2e-tests/tests/instance-performance-tab.test.ts b/packages/compass-e2e-tests/tests/instance-performance-tab.test.ts index dcf093c0162..219c41d5aef 100644 --- a/packages/compass-e2e-tests/tests/instance-performance-tab.test.ts +++ b/packages/compass-e2e-tests/tests/instance-performance-tab.test.ts @@ -5,7 +5,7 @@ import { screenshotIfFailed, skipForWeb, TEST_COMPASS_WEB, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -19,10 +19,12 @@ describe('Instance performance tab', function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); await browser.navigateToConnectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'Performance' ); }); diff --git a/packages/compass-e2e-tests/tests/instance-sidebar.test.ts b/packages/compass-e2e-tests/tests/instance-sidebar.test.ts index 1003e3b077f..0eba8eeb5b6 100644 --- a/packages/compass-e2e-tests/tests/instance-sidebar.test.ts +++ b/packages/compass-e2e-tests/tests/instance-sidebar.test.ts @@ -5,10 +5,10 @@ import { init, cleanup, screenshotIfFailed, - DEFAULT_CONNECTION_STRING, + DEFAULT_CONNECTION_STRING_1, skipForWeb, TEST_MULTIPLE_CONNECTIONS, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -24,12 +24,16 @@ describe('Instance sidebar', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); - connectionId = await browser.getConnectionIdByName(DEFAULT_CONNECTION_NAME); + await browser.disconnectAll(); + await browser.connectToDefaults(); + connectionId = await browser.getConnectionIdByName( + DEFAULT_CONNECTION_NAME_1 + ); }); after(async function () { @@ -45,7 +49,7 @@ describe('Instance sidebar', function () { if (TEST_MULTIPLE_CONNECTIONS) { await browser.selectConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Selectors.Multiple.ClusterInfoItem ); } else { @@ -56,8 +60,6 @@ describe('Instance sidebar', function () { const modal = await browser.$(Selectors.ConnectionInfoModal); await modal.waitForDisplayed(); - await browser.screenshot('connection-info-modal.png'); - await browser.clickVisible(Selectors.ConnectionInfoModalCloseButton); await modal.waitForDisplayed({ reverse: true }); }); @@ -113,8 +115,10 @@ describe('Instance sidebar', function () { await browser.waitUntil(async () => { const treeItems = await browser.$$(Selectors.SidebarTreeItems); - // connection, database, collection for multiple connections, otherwise just database and collection - const expectedCount = TEST_MULTIPLE_CONNECTIONS ? 3 : 2; + // connection, database, collection for multiple connections (twice + // because there are two connections), otherwise just database and + // collection + const expectedCount = TEST_MULTIPLE_CONNECTIONS ? 6 : 2; return treeItems.length === expectedCount; }); @@ -163,7 +167,7 @@ describe('Instance sidebar', function () { // active/highlighted and then the add button and three dot menu will // display without needing to hover await browser.navigateToConnectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'Databases' ); } @@ -171,7 +175,7 @@ describe('Instance sidebar', function () { // open the create database modal from the sidebar if (TEST_MULTIPLE_CONNECTIONS) { await browser.selectConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Sidebar.CreateDatabaseButton, false ); @@ -196,7 +200,7 @@ describe('Instance sidebar', function () { ); await collectionElement.waitForDisplayed(); - await browser.dropDatabaseFromSidebar(DEFAULT_CONNECTION_NAME, dbName); + await browser.dropDatabaseFromSidebar(DEFAULT_CONNECTION_NAME_1, dbName); }); it('can create a collection and drop it', async function () { @@ -224,7 +228,7 @@ describe('Instance sidebar', function () { await browser.$(tabSelectedSelector).waitForDisplayed(); await browser.dropCollectionFromSidebar( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, dbName, collectionName ); @@ -243,7 +247,7 @@ describe('Instance sidebar', function () { ); await numbersCollectionElement.waitForDisplayed(); - const mongoClient = new MongoClient(DEFAULT_CONNECTION_STRING); + const mongoClient = new MongoClient(DEFAULT_CONNECTION_STRING_1); await mongoClient.connect(); try { const database = mongoClient.db(db); @@ -254,7 +258,7 @@ describe('Instance sidebar', function () { if (TEST_MULTIPLE_CONNECTIONS) { await browser.selectConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Selectors.Multiple.RefreshDatabasesItem ); } else { diff --git a/packages/compass-e2e-tests/tests/logging.test.ts b/packages/compass-e2e-tests/tests/logging.test.ts index b07f03c006e..d7be6342056 100644 --- a/packages/compass-e2e-tests/tests/logging.test.ts +++ b/packages/compass-e2e-tests/tests/logging.test.ts @@ -5,7 +5,7 @@ import { screenshotIfFailed, skipForWeb, TEST_MULTIPLE_CONNECTIONS, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import { startTelemetryServer } from '../helpers/telemetry'; @@ -33,9 +33,9 @@ describe('Logging and Telemetry integration', function () { await browser.navigateToMyQueries(); } - await browser.shellEval(DEFAULT_CONNECTION_NAME, 'use test'); + await browser.shellEval(DEFAULT_CONNECTION_NAME_1, 'use test'); await browser.shellEval( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'db.runCommand({ connectionStatus: 1 })' ); } finally { @@ -411,8 +411,9 @@ describe('Logging and Telemetry integration', function () { before(async function () { telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); + const { browser } = compass; - await compass.browser.setFeature('telemetryAtlasUserId', auid); + await browser.setFeature('telemetryAtlasUserId', auid); }); afterEach(async function () { diff --git a/packages/compass-e2e-tests/tests/my-queries-tab.test.ts b/packages/compass-e2e-tests/tests/my-queries-tab.test.ts index eb0c7d5a6de..b468e2581cf 100644 --- a/packages/compass-e2e-tests/tests/my-queries-tab.test.ts +++ b/packages/compass-e2e-tests/tests/my-queries-tab.test.ts @@ -8,7 +8,7 @@ import { skipForWeb, TEST_COMPASS_WEB, TEST_MULTIPLE_CONNECTIONS, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -49,10 +49,11 @@ describe('My Queries tab', function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.disconnectAll(); }); after(async function () { if (TEST_COMPASS_WEB) { @@ -66,12 +67,14 @@ describe('My Queries tab', function () { }); it('opens a saved query', async function () { + await browser.connectByName(DEFAULT_CONNECTION_NAME_1); + const favoriteQueryName = 'list of numbers greater than 10 - query'; const newFavoriteQueryName = 'my renamed query'; // Run a query await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' @@ -99,7 +102,10 @@ describe('My Queries tab', function () { await browser.clickVisible(Selectors.QueryHistorySaveFavoriteItemButton); await browser.closeWorkspaceTabs(); - await browser.navigateToConnectionTab(DEFAULT_CONNECTION_NAME, 'Databases'); + await browser.navigateToConnectionTab( + DEFAULT_CONNECTION_NAME_1, + 'Databases' + ); await browser.navigateToMyQueries(); // open the menu @@ -142,20 +148,19 @@ describe('My Queries tab', function () { ); await confirmRenameButton.waitForEnabled(); - await browser.screenshot('rename-saved-item-modal.png'); - await confirmRenameButton.click(); await renameModal.waitForDisplayed({ reverse: true }); // rename the collection associated with the query to force the open item modal - await browser.shellEval(DEFAULT_CONNECTION_NAME, 'use test'); + await browser.shellEval(DEFAULT_CONNECTION_NAME_1, 'use test'); await browser.shellEval( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'db.numbers.renameCollection("numbers-renamed")' ); + if (TEST_MULTIPLE_CONNECTIONS) { await browser.selectConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Selectors.Multiple.RefreshDatabasesItem ); @@ -184,8 +189,6 @@ describe('My Queries tab', function () { ); await confirmOpenButton.waitForEnabled(); - await browser.screenshot('open-saved-item-modal.png'); - await confirmOpenButton.click(); await openModal.waitForDisplayed({ reverse: true }); @@ -195,7 +198,10 @@ describe('My Queries tab', function () { // back to my queries await browser.closeWorkspaceTabs(); - await browser.navigateToConnectionTab(DEFAULT_CONNECTION_NAME, 'Databases'); + await browser.navigateToConnectionTab( + DEFAULT_CONNECTION_NAME_1, + 'Databases' + ); await browser.navigateToMyQueries(); // open the menu @@ -210,19 +216,19 @@ describe('My Queries tab', function () { ); await confirmDeleteButton.waitForEnabled(); - await browser.screenshot('delete-saved-item-modal.png'); - await confirmDeleteButton.click(); await renameModal.waitForDisplayed({ reverse: true }); }); it('opens a saved aggregation', async function () { + await browser.connectByName(DEFAULT_CONNECTION_NAME_1); + const savedAggregationName = 'list of numbers greater than 10 - aggregation'; // Navigate to aggregation await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Aggregations' @@ -254,8 +260,6 @@ describe('My Queries tab', function () { savedAggregationName ); - await browser.screenshot('save-pipeline-modal.png'); - // click save button const createButton = await browser .$(Selectors.SavePipelineModal) @@ -278,11 +282,13 @@ describe('My Queries tab', function () { const newCollectionName = 'numbers-renamed'; it('users can permanently associate a new namespace for an aggregation/query', async function () { + await browser.connectByName(DEFAULT_CONNECTION_NAME_1); + // save a query and rename the collection associated with the query, so that the query must be opened with the "select namespace" modal // Run a query await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' @@ -313,7 +319,7 @@ describe('My Queries tab', function () { await browser.closeWorkspaceTabs(); await browser.navigateToConnectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'Databases' ); await browser.navigateToMyQueries(); @@ -343,15 +349,15 @@ describe('My Queries tab', function () { } // rename the collection associated with the query to force the open item modal - await browser.shellEval(DEFAULT_CONNECTION_NAME, 'use test'); + await browser.shellEval(DEFAULT_CONNECTION_NAME_1, 'use test'); await browser.shellEval( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, `db.numbers.renameCollection('${newCollectionName}')` ); if (TEST_MULTIPLE_CONNECTIONS) { await browser.selectConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Selectors.Multiple.RefreshDatabasesItem ); } else { diff --git a/packages/compass-e2e-tests/tests/no-network-traffic.test.ts b/packages/compass-e2e-tests/tests/no-network-traffic.test.ts index 7a84b317878..99f4670d228 100644 --- a/packages/compass-e2e-tests/tests/no-network-traffic.test.ts +++ b/packages/compass-e2e-tests/tests/no-network-traffic.test.ts @@ -68,6 +68,8 @@ describe('networkTraffic: false / Isolated Edition', function () { }); const browser = compass.browser; + await browser.setupDefaultConnections(); + { // TODO: Remove this once we are including https://github.com/mongodb-js/mongosh/pull/1349 const exitOnDisconnectFile = path.join(tmpdir, 'exitOnDisconnect.js'); @@ -82,7 +84,7 @@ describe('networkTraffic: false / Isolated Edition', function () { } try { - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); } finally { await cleanup(compass); } diff --git a/packages/compass-e2e-tests/tests/oidc.test.ts b/packages/compass-e2e-tests/tests/oidc.test.ts index 194522c71ca..842bd1b4ffb 100644 --- a/packages/compass-e2e-tests/tests/oidc.test.ts +++ b/packages/compass-e2e-tests/tests/oidc.test.ts @@ -410,11 +410,11 @@ describe('OIDC integration', function () { ); await browser.selectConnection(favoriteName); - await browser.doConnect(); + await browser.doConnect(favoriteName); await browser.disconnectAll(); await browser.selectConnection(favoriteName); - await browser.doConnect(); + await browser.doConnect(favoriteName); await browser.disconnectAll(); const connectionInfo = await getFavoriteConnectionInfo(favoriteName); @@ -435,7 +435,7 @@ describe('OIDC integration', function () { await browser.screenshot(`after-creating-favourite-${favoriteName}.png`); await browser.selectConnection(favoriteName); - await browser.doConnect(); + await browser.doConnect(favoriteName); await browser.disconnectAll(); await browser.screenshot( @@ -444,7 +444,7 @@ describe('OIDC integration', function () { // TODO(COMPASS-7810): when clicking on the favourite the element is somehow stale and then webdriverio throws await browser.selectConnection(favoriteName); - await browser.doConnect(); + await browser.doConnect(favoriteName); await browser.disconnectAll(); const connectionInfo = await getFavoriteConnectionInfo(favoriteName); @@ -463,7 +463,7 @@ describe('OIDC integration', function () { ); await browser.selectConnection(favoriteName); - await browser.doConnect(); + await browser.doConnect(favoriteName); await browser.disconnectAll(); const connectionInfo = await getFavoriteConnectionInfo(favoriteName); @@ -479,7 +479,7 @@ describe('OIDC integration', function () { } await browser.selectConnection(favoriteName); - await browser.doConnect(); + await browser.doConnect(favoriteName); await browser.disconnectAll(); expect(oidcMockProviderEndpointAccesses['/authorize']).to.equal(1); diff --git a/packages/compass-e2e-tests/tests/read-only.test.ts b/packages/compass-e2e-tests/tests/read-only.test.ts index 4e869b0a1ad..c99fd6989bc 100644 --- a/packages/compass-e2e-tests/tests/read-only.test.ts +++ b/packages/compass-e2e-tests/tests/read-only.test.ts @@ -4,7 +4,7 @@ import { screenshotIfFailed, skipForWeb, TEST_MULTIPLE_CONNECTIONS, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import { expect } from 'chai'; import * as Selectors from '../helpers/selectors'; @@ -25,6 +25,7 @@ describe('readOnly: true / Read-Only Edition', function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; await browser.setFeature('readOnly', false); + await browser.setupDefaultConnections(); }); afterEach(async function () { @@ -40,14 +41,14 @@ describe('readOnly: true / Read-Only Edition', function () { ? Selectors.Multiple : Selectors.Single; await browser.setFeature('readOnly', true); - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); if (TEST_MULTIPLE_CONNECTIONS) { // navigate to the databases tab so that the connection is // active/highlighted and then the add button and three dot menu will // display without needing to hover await browser.navigateToConnectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'Databases' ); } @@ -55,7 +56,7 @@ describe('readOnly: true / Read-Only Edition', function () { if (TEST_MULTIPLE_CONNECTIONS) { expect( await browser.hasConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Sidebar.CreateDatabaseButton, false ) @@ -79,7 +80,7 @@ describe('readOnly: true / Read-Only Edition', function () { if (TEST_MULTIPLE_CONNECTIONS) { await browser.navigateToConnectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'Databases' ); } @@ -87,7 +88,7 @@ describe('readOnly: true / Read-Only Edition', function () { if (TEST_MULTIPLE_CONNECTIONS) { expect( await browser.hasConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Sidebar.CreateDatabaseButton, false ) @@ -101,10 +102,10 @@ describe('readOnly: true / Read-Only Edition', function () { it('shows and hides the plus icon on the siderbar to create a collection', async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); const connectionId = await browser.getConnectionIdByName( - DEFAULT_CONNECTION_NAME + DEFAULT_CONNECTION_NAME_1 ); const dbName = 'test'; // existing db @@ -143,9 +144,12 @@ describe('readOnly: true / Read-Only Edition', function () { }); it('shows and hides the create database button on the instance tab', async function () { - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); - await browser.navigateToConnectionTab(DEFAULT_CONNECTION_NAME, 'Databases'); + await browser.navigateToConnectionTab( + DEFAULT_CONNECTION_NAME_1, + 'Databases' + ); let instanceCreateDatabaseButton = await browser.$( Selectors.InstanceCreateDatabaseButton @@ -175,10 +179,10 @@ describe('readOnly: true / Read-Only Edition', function () { it('shows and hides the create collection button on the instance tab', async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); await browser.navigateToDatabaseCollectionsTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test' ); @@ -210,10 +214,10 @@ describe('readOnly: true / Read-Only Edition', function () { it('shows and hides the add data button on the documents tab', async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' @@ -241,11 +245,11 @@ describe('readOnly: true / Read-Only Edition', function () { it('shows and hides the $out aggregation stage', async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); // Some tests navigate away from the numbers collection aggregations tab await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Aggregations' @@ -295,10 +299,10 @@ describe('readOnly: true / Read-Only Edition', function () { it('shows and hides the create index button', async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Indexes' @@ -330,10 +334,10 @@ describe('readOnly: true / Read-Only Edition', function () { it('enables and disables validation actions', async function () { await createNumbersCollection(); - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Validation' diff --git a/packages/compass-e2e-tests/tests/search-indexes.test.ts b/packages/compass-e2e-tests/tests/search-indexes.test.ts index e507cfcbccc..723fb876c30 100644 --- a/packages/compass-e2e-tests/tests/search-indexes.test.ts +++ b/packages/compass-e2e-tests/tests/search-indexes.test.ts @@ -3,11 +3,11 @@ import { init, cleanup, screenshotIfFailed, - DEFAULT_CONNECTION_STRING, Selectors, serverSatisfies, skipForWeb, TEST_COMPASS_WEB, + DEFAULT_CONNECTION_STRING_1, connectionNameFromString, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; @@ -22,7 +22,7 @@ type Connection = { const connectionsWithNoSearchSupport: Connection[] = [ { name: 'Local Connection', - connectionString: DEFAULT_CONNECTION_STRING, + connectionString: DEFAULT_CONNECTION_STRING_1, }, { name: 'Atlas Free Cluster', @@ -192,6 +192,7 @@ describe('Search Indexes', function () { await dbInstance.createCollection(collectionName); } + await browser.disconnectAll(); await browser.connectWithConnectionString(currentConnectionString); await browser.navigateToCollectionTab( connectionNameFromString(currentConnectionString), diff --git a/packages/compass-e2e-tests/tests/shell.test.ts b/packages/compass-e2e-tests/tests/shell.test.ts index b9f52492c2e..742bb7f8acb 100644 --- a/packages/compass-e2e-tests/tests/shell.test.ts +++ b/packages/compass-e2e-tests/tests/shell.test.ts @@ -7,7 +7,7 @@ import { screenshotIfFailed, skipForWeb, TEST_COMPASS_WEB, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, TEST_MULTIPLE_CONNECTIONS, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; @@ -27,6 +27,11 @@ describe('Shell', function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; await browser.setFeature('enableShell', true); + await browser.setupDefaultConnections(); + }); + + beforeEach(async function () { + await browser.disconnectAll(); }); after(async function () { @@ -44,9 +49,9 @@ describe('Shell', function () { }); it('has an info modal', async function () { - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); - await browser.openShell(DEFAULT_CONNECTION_NAME); + await browser.openShell(DEFAULT_CONNECTION_NAME_1); await browser.clickVisible(Selectors.ShellInfoButton); const infoModalElement = await browser.$(Selectors.ShellInfoModal); @@ -55,16 +60,16 @@ describe('Shell', function () { await browser.clickVisible(Selectors.ShellInfoModalCloseButton); await infoModalElement.waitForDisplayed({ reverse: true }); - await browser.closeShell(DEFAULT_CONNECTION_NAME); + await browser.closeShell(DEFAULT_CONNECTION_NAME_1); }); it('shows and hides shell based on settings', async function () { - await browser.connectWithConnectionString(); + await browser.connectToDefaults(); if (TEST_MULTIPLE_CONNECTIONS) { expect( await browser.hasConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Selectors.Multiple.OpenShellItem ) ).to.be.equal(true); @@ -87,7 +92,7 @@ describe('Shell', function () { if (TEST_MULTIPLE_CONNECTIONS) { expect( await browser.hasConnectionMenuItem( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, Selectors.Multiple.OpenShellItem ) ).to.be.equal(false); diff --git a/packages/compass-e2e-tests/tests/tabs.test.ts b/packages/compass-e2e-tests/tests/tabs.test.ts index 50026c3f679..220ed6e3d66 100644 --- a/packages/compass-e2e-tests/tests/tabs.test.ts +++ b/packages/compass-e2e-tests/tests/tabs.test.ts @@ -3,7 +3,9 @@ import { init, cleanup, screenshotIfFailed, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, + DEFAULT_CONNECTION_NAME_2, + TEST_MULTIPLE_CONNECTIONS, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; @@ -19,6 +21,7 @@ describe('Global Tabs', function () { before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + await browser.setupDefaultConnections(); }); beforeEach(async function () { @@ -27,7 +30,8 @@ describe('Global Tabs', function () { await createNumbersCollection(collName, 1); await createNumbersCollection(collName, 1); } - await browser.connectWithConnectionString(); + await browser.disconnectAll(); + await browser.connectToDefaults(); }); afterEach(async function () { @@ -41,7 +45,7 @@ describe('Global Tabs', function () { it('should open tabs over each other when not modified', async function () { for (const collName of collections) { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', collName, 'Documents', @@ -54,7 +58,7 @@ describe('Global Tabs', function () { it('should open new tabs when modified', async function () { for (const collName of collections) { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', collName, 'Documents', @@ -71,7 +75,7 @@ describe('Global Tabs', function () { it('should close tabs without warning even when "modified" by interacting with the tab', async function () { for (const collName of collections) { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', collName, 'Documents', @@ -88,7 +92,7 @@ describe('Global Tabs', function () { it('should ask for confirmation when closing modified Aggregations tab', async function () { await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'a', 'Aggregations' @@ -105,7 +109,7 @@ describe('Global Tabs', function () { await browser.hover( Selectors.workspaceTab({ - connectionName: DEFAULT_CONNECTION_NAME, + connectionName: DEFAULT_CONNECTION_NAME_1, namespace: 'test.a', }) ); @@ -124,7 +128,7 @@ describe('Global Tabs', function () { await browser.hover( Selectors.workspaceTab({ - connectionName: DEFAULT_CONNECTION_NAME, + connectionName: DEFAULT_CONNECTION_NAME_1, namespace: 'test.a', }) ); @@ -141,4 +145,96 @@ describe('Global Tabs', function () { // When confirmed, should remove the tab expect(await browser.$$(Selectors.workspaceTab())).to.have.lengthOf(0); }); + + it("should close a connection's tabs when disconnecting", async function () { + if (!TEST_MULTIPLE_CONNECTIONS) { + this.skip(); + } + + // workspace 1: connection 1, Documents tab + await browser.navigateToCollectionTab( + DEFAULT_CONNECTION_NAME_1, + 'test', + 'a', + 'Documents', + false + ); + + // Click something to make sure we "modified" tab 1 + await browser.clickVisible( + Selectors.queryBarApplyFilterButton('Documents') + ); + + // workspace 2: connection 2, Documents tab + await browser.navigateToCollectionTab( + DEFAULT_CONNECTION_NAME_2, + 'test', + 'a', + 'Documents', + false + ); + + // Click something to make sure we "modified" tab 2 + await browser.clickVisible( + Selectors.queryBarApplyFilterButton('Documents') + ); + + // workspace 3: My Qeries + await browser.navigateToMyQueries(); + + const workspace1Options = { + connectionName: DEFAULT_CONNECTION_NAME_1, + type: 'Collection', + namespace: 'test.a', + }; + + // check that they are all there + + expect( + await browser.$(Selectors.workspaceTab(workspace1Options)).isExisting() + ).to.be.true; + + const workspace2Options = { + connectionName: DEFAULT_CONNECTION_NAME_2, + type: 'Collection', + namespace: 'test.a', + }; + + expect( + await browser.$(Selectors.workspaceTab(workspace2Options)).isExisting() + ).to.be.true; + + const workspace3Options = { + type: 'My Queries', + }; + + expect( + await browser.$(Selectors.workspaceTab(workspace3Options)).isExisting() + ).to.be.true; + + // disconnect one connection + + await browser.disconnectByName(DEFAULT_CONNECTION_NAME_1); + + // the workspace for connection 1 should go away + await browser.waitUntil(async () => { + const exists = await browser + .$(Selectors.workspaceTab(workspace1Options)) + .isExisting(); + return exists === false; + }); + + // give it a moment in case it takes time for workspaces to go away + await browser.pause(1000); + + // the workspace for connection 2 should still be there + expect( + await browser.$(Selectors.workspaceTab(workspace2Options)).isExisting() + ).to.be.true; + + // the My Queries workspace should still be there + expect( + await browser.$(Selectors.workspaceTab(workspace3Options)).isExisting() + ).to.be.true; + }); }); diff --git a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts index 30f4f35284b..c0201e9df4e 100644 --- a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts +++ b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts @@ -3,7 +3,7 @@ import { init, cleanup, screenshotIfFailed, - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import { createNumbersCollection } from '../helpers/insert-data'; @@ -45,7 +45,7 @@ describe('Time to first query', function () { await browser.connectWithConnectionString(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents' @@ -70,7 +70,7 @@ describe('Time to first query', function () { await browser.connectWithConnectionString(); await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME, + DEFAULT_CONNECTION_NAME_1, 'test', 'numbers', 'Documents'