From e79ee1859eb48a37b11786c44bed75de4f59e934 Mon Sep 17 00:00:00 2001 From: mmeigs Date: Wed, 12 Jul 2023 15:40:55 -0400 Subject: [PATCH] DOP-3079: de-hardcode DeprecatedVersionSelector (#852) --- package-lock.json | 129 ++++--------------- package.json | 2 + src/components/DeprecatedVersionSelector.js | 86 ++++++++----- src/utils/get-site-url.js | 19 --- tests/unit/DeprecatedVersionSelector.test.js | 105 +++++++++++---- tests/unit/utils/get-site-url.test.js | 17 --- 6 files changed, 156 insertions(+), 202 deletions(-) delete mode 100644 src/utils/get-site-url.js delete mode 100644 tests/unit/utils/get-site-url.test.js diff --git a/package-lock.json b/package-lock.json index 127b1cad6..1e78d5531 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,6 +65,8 @@ "https-browserify": "^1.0.0", "immer": "^9.0.6", "json-schema": "^0.4.0", + "lodash.isempty": "^4.4.0", + "lodash.keyby": "^4.6.0", "minimist": "^1.2.6", "mobx": "^6.1.5", "mongodb-stitch-browser-sdk": "^4.8.0", @@ -17231,6 +17233,11 @@ "version": "3.0.3", "license": "MIT" }, + "node_modules/lodash.isempty": { + "version": "4.4.0", + "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" + }, "node_modules/lodash.isequal": { "version": "4.5.0", "license": "MIT" @@ -17251,6 +17258,11 @@ "version": "4.0.1", "license": "MIT" }, + "node_modules/lodash.keyby": { + "version": "4.6.0", + "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/lodash.keyby/-/lodash.keyby-4.6.0.tgz", + "integrity": "sha512-PRe4Cn20oJM2Sn6ljcZMeKgyhTHpzvzFmdsp9rK+6K0eJs6Tws0MqgGFpfX/o2HjcoQcBny1Eik9W7BnVTzjIQ==" + }, "node_modules/lodash.map": { "version": "4.6.0", "license": "MIT" @@ -26452,113 +26464,6 @@ } } }, - "@leafygreen-ui/search-input": { - "version": "2.0.8", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/search-input/-/search-input-2.0.8.tgz", - "integrity": "sha512-ijMpZgyUpOQM1fOnUs1XavIYAcD9P5PNRWp2hprgWMJZp8FrcopsslMJJIDaM00Sdj0MjSDAti9+aXeo+2ZgSw==", - "requires": { - "@leafygreen-ui/a11y": "^1.4.3", - "@leafygreen-ui/emotion": "^4.0.4", - "@leafygreen-ui/hooks": "^7.7.5", - "@leafygreen-ui/icon": "^11.17.0", - "@leafygreen-ui/icon-button": "^15.0.12", - "@leafygreen-ui/input-option": "^1.0.5", - "@leafygreen-ui/lib": "^10.4.0", - "@leafygreen-ui/palette": "^4.0.4", - "@leafygreen-ui/polymorphic": "^1.3.2", - "@leafygreen-ui/popover": "^11.0.12", - "@leafygreen-ui/tokens": "^2.1.1", - "@leafygreen-ui/typography": "^16.5.1", - "lodash": "^4.17.21", - "polished": "^4.2.2" - }, - "dependencies": { - "@leafygreen-ui/hooks": { - "version": "7.7.5", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/hooks/-/hooks-7.7.5.tgz", - "integrity": "sha512-Spk36yndhplGfCdLl14RWuoDPGYfEgA6AwqbaI4T45i+A2QtDTvdSFyMUkNmDK5vzk5cWmR/SmkRS/o5T9zgMA==", - "requires": { - "lodash": "^4.17.21" - } - }, - "@leafygreen-ui/icon-button": { - "version": "15.0.12", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/icon-button/-/icon-button-15.0.12.tgz", - "integrity": "sha512-19tXprK4/jIZq19o6Y2OnPw5ifuLIhpxAJ4/gs/lIFk/4A5PZJOnZzy6ABdiMaFk/1miSjoDQ5B+lncvgAx1gA==", - "requires": { - "@leafygreen-ui/a11y": "^1.4.2", - "@leafygreen-ui/box": "^3.1.4", - "@leafygreen-ui/emotion": "^4.0.4", - "@leafygreen-ui/icon": "^11.17.0", - "@leafygreen-ui/lib": "^10.4.0", - "@leafygreen-ui/palette": "^4.0.4", - "@leafygreen-ui/tokens": "^2.1.1" - } - }, - "@leafygreen-ui/lib": { - "version": "10.4.0", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/lib/-/lib-10.4.0.tgz", - "integrity": "sha512-gGZBJ0Mjo2/hHfECbERGJbx1nPFNDqkge7L1K5y5LwBjpiOYjUNa1OsyBRwc9pr+zucdAF2FHSo+EdoT83Mbtg==", - "requires": { - "@storybook/csf": "^0.1.0", - "lodash": "^4.17.21", - "prop-types": "^15.7.2" - } - }, - "@leafygreen-ui/palette": { - "version": "4.0.4", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/palette/-/palette-4.0.4.tgz", - "integrity": "sha512-nuZy2RtKHAGpIKrDduqC8P8PvajJRT1hQURoisYMiB32NmEEHGEhtHw4MlS+Kv92HFA0jxgMdZUHKTXq83BhjA==" - }, - "@leafygreen-ui/popover": { - "version": "11.0.12", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/popover/-/popover-11.0.12.tgz", - "integrity": "sha512-zF8axGB+RhpoG0xgb4K/p8ydS5JAEXpwqdLrcrY9r7cAYzxX0F4mhIpYg+oyK2K5TIbgGR/fsFPbhKYsmreuMA==", - "requires": { - "@leafygreen-ui/emotion": "^4.0.4", - "@leafygreen-ui/hooks": "^7.7.5", - "@leafygreen-ui/lib": "^10.4.0", - "@leafygreen-ui/portal": "^4.1.4", - "@leafygreen-ui/tokens": "^2.1.1", - "react-transition-group": "^4.4.1" - } - }, - "@leafygreen-ui/tokens": { - "version": "2.1.1", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/tokens/-/tokens-2.1.1.tgz", - "integrity": "sha512-5mpyxRhp+8aY6eX6UYacDV6PLl3OieWG061ceAn2ThYaanIP64AMbodWifrF/IFMItJPFkwAtfPtJqugOFVb7Q==", - "requires": { - "@leafygreen-ui/palette": "^4.0.4" - } - }, - "@leafygreen-ui/typography": { - "version": "16.5.1", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/typography/-/typography-16.5.1.tgz", - "integrity": "sha512-5dentOt4LubXxmkwVVy6ZlUvGsebJsHhZZ9LAlplUz13VkyvS458Bhnog4cqNqvnJjdlWLdO+ZO0/JyYUIIuXw==", - "requires": { - "@leafygreen-ui/emotion": "^4.0.4", - "@leafygreen-ui/icon": "^11.17.0", - "@leafygreen-ui/lib": "^10.4.0", - "@leafygreen-ui/palette": "^4.0.4", - "@leafygreen-ui/polymorphic": "^1.3.2", - "@leafygreen-ui/tokens": "^2.1.1" - } - }, - "@storybook/csf": { - "version": "0.1.1", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@storybook/csf/-/csf-0.1.1.tgz", - "integrity": "sha512-4hE3AlNVxR60Wc5KSC68ASYzUobjPqtSKyhV6G+ge0FIXU55N5nTY7dXGRZHQGDBPq+XqchMkIdlkHPRs8nTHg==", - "requires": { - "type-fest": "^2.19.0" - } - }, - "type-fest": { - "version": "2.19.0", - "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" - } - } - }, "@leafygreen-ui/segmented-control": { "version": "8.0.1", "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/@leafygreen-ui/segmented-control/-/segmented-control-8.0.1.tgz", @@ -34799,6 +34704,11 @@ "lodash.isboolean": { "version": "3.0.3" }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" + }, "lodash.isequal": { "version": "4.5.0" }, @@ -34814,6 +34724,11 @@ "lodash.isstring": { "version": "4.0.1" }, + "lodash.keyby": { + "version": "4.6.0", + "resolved": "https://artifactory.corp.mongodb.com/artifactory/api/npm/npm/lodash.keyby/-/lodash.keyby-4.6.0.tgz", + "integrity": "sha512-PRe4Cn20oJM2Sn6ljcZMeKgyhTHpzvzFmdsp9rK+6K0eJs6Tws0MqgGFpfX/o2HjcoQcBny1Eik9W7BnVTzjIQ==" + }, "lodash.map": { "version": "4.6.0" }, diff --git a/package.json b/package.json index 8cccb5f5e..286bbd416 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,8 @@ "https-browserify": "^1.0.0", "immer": "^9.0.6", "json-schema": "^0.4.0", + "lodash.isempty": "^4.4.0", + "lodash.keyby": "^4.6.0", "minimist": "^1.2.6", "mobx": "^6.1.5", "mongodb-stitch-browser-sdk": "^4.8.0", diff --git a/src/components/DeprecatedVersionSelector.js b/src/components/DeprecatedVersionSelector.js index 79fd54270..c74c09670 100644 --- a/src/components/DeprecatedVersionSelector.js +++ b/src/components/DeprecatedVersionSelector.js @@ -1,11 +1,15 @@ import React, { useCallback, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import queryString from 'query-string'; +import keyBy from 'lodash.keyby'; +import isEmpty from 'lodash.isempty'; import Button from '@leafygreen-ui/button'; import { css, cx } from '@leafygreen-ui/emotion'; -import { getSiteUrl } from '../utils/get-site-url'; import { isBrowser } from '../utils/is-browser'; import { theme } from '../theme/docsTheme'; +import { fetchDocuments } from '../utils/realm'; +import { useSiteMetadata } from '../hooks/use-site-metadata'; +import { BRANCHES_COLLECTION } from '../build-constants'; import Select from './Select'; const SELECT_WIDTH = '336px'; @@ -19,30 +23,6 @@ const selectStyle = css` } `; -const PROPERTY_NAME_MAPPING = { - 'atlas-open-service-broker': 'MongoDB Atlas Open Service Broker on Kubernetes', - 'atlas-cli': 'MongoDB Atlas CLI', - 'bi-connector': 'MongoDB Connector for BI', - charts: 'MongoDB Charts', - cloud: 'MongoDB Atlas', - compass: 'MongoDB Compass', - docs: 'MongoDB Server', - drivers: 'MongoDB Drivers', - 'kafka-connector': 'MongoDB Kafka Connector', - 'kubernetes-operator': 'MongoDB Enterprise Kubernetes Operator', - mongocli: 'MongoDB CLI', - mongoid: 'Mongoid', - mms: 'MongoDB Ops Manager', - 'ruby-driver': 'MongoDB Ruby Driver', - 'spark-connector': 'MongoDB Connector for Spark', -}; - -const fullProductName = (property) => { - if (!property) return null; - // Display full product name on product dropdown - return PROPERTY_NAME_MAPPING[property.replace('_', '-')] || property; -}; - const isPrimaryBranch = (version) => { return version === 'main' || version === 'master'; }; @@ -64,9 +44,34 @@ const isVersioned = (versionOptions) => { return !(versionOptions.length === 1 && isPrimaryBranch(versionOptions[0])); }; +// Validation for necessary url fields to bypass errors +const hasValidHostName = (repoDocument) => { + if (!repoDocument?.url?.dotcomprd || !repoDocument?.prefix?.dotcomprd) return false; + return true; +}; + +// Add mms-docs to reposMap. It does not have a document in repos_branches collection. +// TODO: Remove when mms-docs is added to repos_branches +const addOldGenToReposMap = (reposMap) => { + const oldGenRepos = { + mms: { + displayName: 'MongoDB Ops Manager', + url: { dotcomprd: 'http://mongodb.com/' }, + prefix: { dotcomprd: 'docs/ops-manager' }, + }, + }; + return { + ...oldGenRepos, + ...reposMap, + }; +}; + const DeprecatedVersionSelector = ({ metadata: { deprecated_versions: deprecatedVersions } }) => { + const { reposDatabase } = useSiteMetadata(); const [product, setProduct] = useState(''); const [version, setVersion] = useState(''); + const [reposMap, setReposMap] = useState({}); + const updateProduct = useCallback(({ value }) => { setProduct(value); setVersion(''); @@ -74,6 +79,17 @@ const DeprecatedVersionSelector = ({ metadata: { deprecated_versions: deprecated const updateVersion = useCallback(({ value }) => setVersion(value), []); const buttonDisabled = !(product && version); + // Fetch repos_branches for `displayName` and url + useEffect(() => { + if (reposDatabase) { + fetchDocuments(reposDatabase, BRANCHES_COLLECTION).then((resp) => { + const reposBranchesMap = keyBy(resp, 'project'); + const reposBranchesMapWithOldGen = addOldGenToReposMap(reposBranchesMap); + setReposMap(reposBranchesMapWithOldGen); + }); + } + }, [reposDatabase]); + useEffect(() => { if (isBrowser) { // Extract the value of 'site' query string from the page url to pre-select product @@ -87,23 +103,25 @@ const DeprecatedVersionSelector = ({ metadata: { deprecated_versions: deprecated const generateUrl = () => { // Our current LG button version has a bug where a disabled button with an href allows the disabled // button to be clickable. This logic can be removed when LG button is version >= 12.0.4. - if (buttonDisabled) { + if (buttonDisabled || isEmpty(reposMap) || !hasValidHostName(reposMap[product])) { return null; } + // Utilizing hardcoded env because legacy sites are not available on dev/stage + const hostName = reposMap[product].url.dotcomprd + reposMap[product].prefix.dotcomprd; const versionOptions = deprecatedVersions[product]; - const hostName = getSiteUrl(product); const versionName = isVersioned(versionOptions) ? version : ''; - return ['docs', 'mms', 'cloud-docs', 'atlas-cli'].includes(product) - ? `${hostName}/${versionName}` - : `${hostName}/${product}/${versionName}`; + return `${hostName}/${versionName}`; }; const productChoices = deprecatedVersions - ? Object.keys(deprecatedVersions).map((product) => ({ - text: fullProductName(product), - value: product, - })) + ? Object.keys(deprecatedVersions) + .map((product) => ({ + text: reposMap[product]?.displayName, + value: product, + })) + // Ensure invalid entries do not break selector + .filter(({ text }) => !!text) : []; const versionChoices = deprecatedVersions[product] diff --git a/src/utils/get-site-url.js b/src/utils/get-site-url.js deleted file mode 100644 index aed0f8d43..000000000 --- a/src/utils/get-site-url.js +++ /dev/null @@ -1,19 +0,0 @@ -// Given a project's `name`, return its base URL. -const getSiteUrl = (project) => { - let url = 'https://www.mongodb.com/docs'; - switch (project) { - case 'cloud-docs': - url = 'https://www.mongodb.com/docs/atlas'; - break; - case 'atlas-cli': - url = 'https://www.mongodb.com/docs/atlas/cli'; - break; - case 'mms': - url = 'https://www.mongodb.com/docs/ops-manager'; - break; - default: - } - return url; -}; - -module.exports.getSiteUrl = getSiteUrl; diff --git a/tests/unit/DeprecatedVersionSelector.test.js b/tests/unit/DeprecatedVersionSelector.test.js index 9dc36b7c0..6fafb90cd 100644 --- a/tests/unit/DeprecatedVersionSelector.test.js +++ b/tests/unit/DeprecatedVersionSelector.test.js @@ -1,7 +1,8 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import DeprecatedVersionSelector from '../../src/components/DeprecatedVersionSelector'; +import * as realm from '../../src/utils/realm'; const deprecatedVersions = { docs: ['v2.2', 'v2.4', 'v2.6', 'v3.0', 'v3.2', 'v3.4'], @@ -14,33 +15,85 @@ const metadata = { deprecated_versions: deprecatedVersions, }; +const mockedReposBranches = [ + { + project: 'docs', + displayName: 'MongoDB Manual', + url: { + dotcomprd: 'https://mongodb.com/', + }, + prefix: { + dotcomprd: 'docs', + }, + }, + { + project: 'mongocli', + displayName: 'MongoDB Command Line Interface', + url: { + dotcomprd: 'https://mongodb.com/', + }, + prefix: { + dotcomprd: 'docs/mongocli', + }, + }, + { + project: 'atlas-open-service-broker', + displayName: 'MongoDB Atlas Open Service Broker on Kubernetes', + url: { + dotcomprd: 'https://mongodb.com/', + }, + prefix: { + dotcomprd: 'docs/atlas-open-service-broker', + }, + }, +]; + +jest.mock('../../src/hooks/use-site-metadata', () => ({ + useSiteMetadata: () => ({ reposDatabase: 'pool_test' }), +})); + describe('DeprecatedVersionSelector when rendered', () => { - jest.useFakeTimers(); + let wrapper, mockFetchDocuments; + + beforeEach(() => { + mockFetchDocuments = jest.spyOn(realm, 'fetchDocuments').mockImplementation(async (dbName, collectionName) => { + return mockedReposBranches; + }); + }); + + afterAll(() => { + mockFetchDocuments.mockClear(); + }); - it('shows two dropdowns', () => { - const wrapper = render(); - const productDropdown = wrapper.queryAllByText('Select a Product'); - const versionDropdown = wrapper.queryAllByText('Select a Version'); + it('shows two dropdowns', async () => { + wrapper = render(); + + const productDropdown = await wrapper.findAllByText('Select a Product'); + const versionDropdown = await wrapper.findAllByText('Select a Version'); expect(productDropdown).toBeTruthy(); expect(versionDropdown).toBeTruthy(); }); - it('shows a disabled submit button', () => { - const wrapper = render(); + it('shows a disabled submit button', async () => { + wrapper = render(); - const button = wrapper.getByTitle('View Documentation'); + const button = await wrapper.findByTitle('View Documentation'); expect(button).toBeTruthy(); expect(button).toBeDisabled(); }); - it('shows a disabled version selector', () => { - const wrapper = render(); + it('shows a disabled version selector', async () => { + wrapper = render(); + + await wrapper.findAllByRole('button'); expect(wrapper.container.querySelectorAll('button')[1]).toHaveAttribute('aria-disabled', 'true'); }); - it('does not show either dropdown menu', () => { - const wrapper = render(); + it('does not show either dropdown menu', async () => { + await waitFor(() => { + wrapper = render(); + }); expect(wrapper.queryAllByText('mms')).toHaveLength(0); expect(wrapper.queryAllByText(deprecatedVersions.mms[0])).toHaveLength(0); @@ -49,18 +102,18 @@ describe('DeprecatedVersionSelector when rendered', () => { // Test product dropdown describe('when the product button is clicked', () => { it('shows the dropdown menu with elements per metadata node', () => { - const wrapper = render(); + wrapper = render(); const productDropdown = wrapper.container.querySelectorAll('button')[0]; userEvent.click(productDropdown); - expect(wrapper.getByText('MongoDB Server')).toBeTruthy(); - expect(wrapper.getByText('MongoDB Ops Manager')).toBeTruthy(); - expect(wrapper.getByText('MongoDB Atlas Open Service Broker on Kubernetes')).toBeTruthy(); + expect(wrapper.findByText('MongoDB Manual')).toBeTruthy(); + expect(wrapper.findByText('MongoDB Ops Manager')).toBeTruthy(); + expect(wrapper.findByText('MongoDB Atlas Open Service Broker on Kubernetes')).toBeTruthy(); }); it('version dropdown text is correct', () => { - const wrapper = render(); + wrapper = render(); const productDropdown = wrapper.container.querySelectorAll('button')[0]; userEvent.click(productDropdown); @@ -70,7 +123,7 @@ describe('DeprecatedVersionSelector when rendered', () => { describe('when the product button is clicked again', () => { it('hides the dropdown menu', () => { - const wrapper = render(); + wrapper = render(); const productDropdown = wrapper.container.querySelectorAll('button')[0]; userEvent.click(productDropdown); @@ -82,23 +135,25 @@ describe('DeprecatedVersionSelector when rendered', () => { describe('when the selected product has a single deprecated version', () => { test.each([ - ['MongoDB CLI', 'Version 0.5.0', 'https://www.mongodb.com/docs/mongocli/v0.5.0'], + ['MongoDB Command Line Interface', 'Version 0.5.0', 'https://mongodb.com/docs/mongocli/v0.5.0'], [ 'MongoDB Atlas Open Service Broker on Kubernetes', 'latest', - 'https://www.mongodb.com/docs/atlas-open-service-broker/', + 'https://mongodb.com/docs/atlas-open-service-broker/', ], - ])('generates the correct docs URL', (product, versionSelection, expectedUrl) => { + ])('generates the correct docs URL', async (product, versionSelection, expectedUrl) => { const wrapper = render(); const productDropdown = wrapper.container.querySelectorAll('button')[0]; userEvent.click(productDropdown); - userEvent.click(wrapper.getByText(product)); + const productOption = await wrapper.findByText(product); + userEvent.click(productOption); const versionDropdown = wrapper.container.querySelectorAll('button')[1]; userEvent.click(versionDropdown); - userEvent.click(wrapper.getByText(versionSelection)); + const versionOption = await wrapper.findByText(versionSelection); + userEvent.click(versionOption); - const button = wrapper.getByTitle('View Documentation'); + const button = await wrapper.findByTitle('View Documentation'); expect(button).toHaveAttribute('aria-disabled', 'false'); expect(button.href).toEqual(expectedUrl); }); diff --git a/tests/unit/utils/get-site-url.test.js b/tests/unit/utils/get-site-url.test.js deleted file mode 100644 index 3c6706481..000000000 --- a/tests/unit/utils/get-site-url.test.js +++ /dev/null @@ -1,17 +0,0 @@ -import { getSiteUrl } from '../../../src/utils/get-site-url'; - -describe('getSiteUrl', () => { - it('returns the prefix for all www. properties, including prefix + project for mms and cloud', () => { - process.env = {}; - Object.defineProperty(window, 'location', { - value: { - hostname: 'www.mongodb.com/docs', - }, - writable: true, - }); - - expect(getSiteUrl('manual')).toBe('https://www.mongodb.com/docs'); - expect(getSiteUrl('mms')).toBe('https://www.mongodb.com/docs/ops-manager'); - expect(getSiteUrl('cloud-docs')).toBe('https://www.mongodb.com/docs/atlas'); - }); -});