Skip to content

Commit eeca40a

Browse files
mabaasitlerouxb
andauthored
tests(search indexes): setup e2e search indexes test COMPASS-7171 (#4832)
Co-authored-by: Le Roux Bodenstein <[email protected]>
1 parent c9014c9 commit eeca40a

File tree

9 files changed

+311
-3
lines changed

9 files changed

+311
-3
lines changed

.evergreen/functions.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ variables:
7777
EVERGREEN_BUCKET_NAME: mciuploads
7878
EVERGREEN_BUCKET_KEY_PREFIX: ${project}/${revision}_${revision_order_id}
7979
MONGODB_RUNNER_LOG_DIR: ${workdir}/src/.testserver/
80+
E2E_TESTS_ATLAS_CS_WITHOUT_SEARCH: ${e2e_tests_atlas_cs_without_search}
81+
E2E_TESTS_ATLAS_CS_WITH_SEARCH: ${e2e_tests_atlas_cs_with_search}
8082

8183
# This is here with the variables because anchors aren't supported across includes
8284
post:

packages/compass-e2e-tests/helpers/commands/create-index.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,22 @@ export async function createIndex(
3434
const indexName = extraOptions?.indexName ?? `${fieldName}_${indexType}`;
3535

3636
// Open the modal
37-
await browser.clickVisible(Selectors.CreateIndexButton);
37+
const isWithCreateDropdown = await browser
38+
.$(Selectors.CreateIndexDropdownButton)
39+
.isExisting();
40+
if (isWithCreateDropdown) {
41+
await browser.waitUntil(async () => {
42+
await browser.clickVisible(Selectors.CreateIndexDropdownButton);
43+
return await browser
44+
.$(Selectors.createIndexDropdownAction('regular-indexes'))
45+
.isExisting();
46+
});
47+
await browser.clickVisible(
48+
Selectors.createIndexDropdownAction('regular-indexes')
49+
);
50+
} else {
51+
await browser.clickVisible(Selectors.CreateIndexButton);
52+
}
3853
const createModal = await browser.$(Selectors.CreateIndexModal);
3954
await createModal.waitForDisplayed();
4055

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { CompassBrowser } from '../compass-browser';
2+
import * as Selectors from '../selectors';
3+
4+
export async function createSearchIndex(
5+
browser: CompassBrowser,
6+
indexName: string,
7+
indexDefinition: string,
8+
screenshotName?: string
9+
): Promise<void> {
10+
// Open modal using dropdown
11+
await browser.clickVisible(Selectors.CreateIndexDropdownButton);
12+
await browser
13+
.$(Selectors.createIndexDropdownAction('search-indexes'))
14+
.waitForDisplayed();
15+
await browser.clickVisible(
16+
Selectors.createIndexDropdownAction('search-indexes')
17+
);
18+
19+
const createModal = await browser.$(Selectors.CreateSearchIndexModal);
20+
await createModal.waitForDisplayed();
21+
22+
await browser.setValueVisible(Selectors.CreateSearchIndexName, indexName);
23+
24+
await browser.setCodemirrorEditorValue(
25+
Selectors.CreateSearchIndexDefinition,
26+
indexDefinition
27+
);
28+
29+
if (screenshotName) {
30+
await browser.screenshot(screenshotName);
31+
}
32+
33+
// Create the index
34+
await browser.clickVisible(Selectors.CreateSearchIndexConfirmButton);
35+
36+
// Assert that modal goes away
37+
await createModal.waitForDisplayed({ reverse: true });
38+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { CompassBrowser } from '../compass-browser';
2+
import * as Selectors from '../selectors';
3+
4+
export async function dropSearchIndex(
5+
browser: CompassBrowser,
6+
indexName: string,
7+
screenshotName?: string
8+
) {
9+
const indexRowSelector = Selectors.searchIndexRow(indexName);
10+
const indexRow = await browser.$(indexRowSelector);
11+
await indexRow.waitForDisplayed();
12+
13+
await browser.hover(indexRowSelector);
14+
await browser.clickVisible(Selectors.searchIndexDropButton(indexName));
15+
16+
const dropModal = await browser.$(Selectors.ConfirmationModal);
17+
await dropModal.waitForDisplayed();
18+
19+
const confirmInput = await browser.$(Selectors.ConfirmationModalInput);
20+
await confirmInput.waitForDisplayed();
21+
await confirmInput.setValue(indexName);
22+
23+
if (screenshotName) {
24+
await browser.screenshot(screenshotName);
25+
}
26+
27+
await browser.clickVisible(Selectors.ConfirmationModalConfirmButton());
28+
await dropModal.waitForDisplayed({ reverse: true });
29+
}

packages/compass-e2e-tests/helpers/commands/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,5 @@ export * from './create-index';
6464
export * from './drop-index';
6565
export * from './hide-index';
6666
export * from './unhide-index';
67+
export * from './create-search-index';
68+
export * from './drop-search-index';

packages/compass-e2e-tests/helpers/commands/navigate-to-collection-tab.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ async function navigateToCollection(
2828
const sidebarFilterInputElement = await browser.$(
2929
Selectors.SidebarFilterInput
3030
);
31+
await sidebarFilterInputElement.clearValue();
3132
await sidebarFilterInputElement.setValue(collectionName);
3233
const collectionElement = await browser.$(collectionSelector);
3334

packages/compass-e2e-tests/helpers/selectors.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,7 @@ export const explainPlanSummaryStat = (
916916
};
917917

918918
// Indexes tab
919+
type IndexesType = 'regular-indexes' | 'search-indexes';
919920
export const IndexList = '[data-testid="indexes-list"]';
920921
export const indexComponent = (name: string): string => {
921922
return `[data-testid="indexes-row-${name}"]`;
@@ -934,10 +935,35 @@ export const indexOptionInput = (
934935
return `[data-testid="create-index-modal-${fieldName}-${type}"]`;
935936
};
936937

938+
export const indexesSegmentedTab = (name: IndexesType) => {
939+
return `[data-testid="indexes-segment-controls"] [data-testid="${name}-tab"] button`;
940+
};
941+
942+
// Search Index
943+
export const SearchIndexList = '[data-testid="search-indexes"]';
944+
export const CreateSearchIndexModal =
945+
'[data-testid="create-search-index-modal"]';
946+
export const CreateSearchIndexName = '[data-testid="name-of-search-index"]';
947+
export const CreateSearchIndexDefinition =
948+
'[data-testid="definition-of-search-index"]';
949+
export const CreateSearchIndexConfirmButton =
950+
'[data-testid="create-search-index-button"]';
951+
export const searchIndexRow = (name: string) =>
952+
`[data-testid="search-indexes-row-${name}"]`;
953+
export const searchIndexDropButton = (name: string) =>
954+
`${searchIndexRow(name)} [data-testid="search-index-actions-drop-action"]`;
955+
937956
// Indexes modal
938957
export const CreateIndexModal = '[data-testid="create-index-modal"]';
939958
export const CreateIndexButton =
940959
'[data-testid="open-create-index-modal-button"]';
960+
export const CreateIndexDropdownButton =
961+
'[data-testid="multiple-index-types-creation-dropdown-show-actions"]';
962+
export const createIndexDropdownAction = (type: IndexesType) => {
963+
const action =
964+
type === 'regular-indexes' ? 'createRegularIndex' : 'createSearchIndex';
965+
return `[data-testid="multiple-index-types-creation-dropdown-${action}-action"]`;
966+
};
941967
export const createIndexModalFieldNameSelectInput = (idx: number): string => {
942968
return `[data-testid="create-index-fields-name-${idx}"] input`;
943969
};
@@ -1090,6 +1116,8 @@ export const ExportToLanguageQueryOutput =
10901116

10911117
// Confirmation modal
10921118
export const ConfirmationModal = '[data-testid="confirmation-modal"]';
1119+
export const ConfirmationModalInput =
1120+
'[data-testid="confirmation-modal"] input';
10931121
export const ConfirmationModalConfirmButton = (
10941122
modalSelector = ConfirmationModal
10951123
) => `${modalSelector} [role=dialog] button:nth-of-type(1)`;
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import type { CompassBrowser } from '../helpers/compass-browser';
2+
import {
3+
beforeTests,
4+
afterTests,
5+
afterTest,
6+
MONGODB_TEST_SERVER_PORT,
7+
Selectors,
8+
serverSatisfies,
9+
} from '../helpers/compass';
10+
import type { Compass } from '../helpers/compass';
11+
import { disconnect } from '../helpers/commands';
12+
import { expect } from 'chai';
13+
import { type Db, MongoClient } from 'mongodb';
14+
15+
type Connection = {
16+
name: string;
17+
connectionString?: string;
18+
};
19+
20+
const connectionsWithNoSearchSupport: Connection[] = [
21+
{
22+
name: 'Local Connection',
23+
connectionString: `mongodb://localhost:${MONGODB_TEST_SERVER_PORT}/test`,
24+
},
25+
{
26+
name: 'Atlas Free Cluster',
27+
connectionString: process.env.E2E_TESTS_ATLAS_CS_WITHOUT_SEARCH,
28+
},
29+
];
30+
const connectionsWithSearchSupport: Connection[] = [
31+
{
32+
name: 'Atlas Dedicated Cluster',
33+
connectionString: process.env.E2E_TESTS_ATLAS_CS_WITH_SEARCH,
34+
},
35+
// todo: atlas local dev
36+
];
37+
38+
const INDEX_DEFINITION = JSON.stringify({
39+
mappings: {
40+
dynamic: true,
41+
},
42+
});
43+
44+
function getRandomNumber() {
45+
return Math.floor(Math.random() * 2 ** 20);
46+
}
47+
48+
describe('Search Indexes', function () {
49+
let compass: Compass;
50+
let browser: CompassBrowser;
51+
let mongoClient: MongoClient;
52+
let dbInstance: Db;
53+
let currentConnectionString: string;
54+
55+
// DB name is always same.
56+
const DB_NAME = 'e2e_indexes_test';
57+
// Collection name is random per every test.
58+
let collectionName: string;
59+
60+
before(async function () {
61+
// $search works with server 4.2 or more
62+
if (!serverSatisfies('>= 4.1.11')) {
63+
this.skip();
64+
}
65+
compass = await beforeTests({
66+
extraSpawnArgs: ['--enableAtlasSearchIndexManagement'],
67+
});
68+
browser = compass.browser;
69+
});
70+
71+
after(async function () {
72+
await afterTests(compass, this.currentTest);
73+
});
74+
75+
beforeEach(async function () {
76+
collectionName = `e2e_coll_numbers_${getRandomNumber()}`;
77+
// Ensure namespace exists
78+
{
79+
mongoClient = new MongoClient(currentConnectionString);
80+
await mongoClient.connect();
81+
dbInstance = mongoClient.db(DB_NAME);
82+
83+
// Try to delete a namespace if it exists
84+
try {
85+
await dbInstance.dropCollection(collectionName);
86+
} catch (e) {
87+
// noop
88+
}
89+
90+
// Create a namespace
91+
await dbInstance.createCollection(collectionName);
92+
}
93+
94+
await browser.connectWithConnectionString(currentConnectionString);
95+
await browser.navigateToCollectionTab(DB_NAME, collectionName, 'Indexes');
96+
});
97+
98+
afterEach(async function () {
99+
// Drop the collection
100+
{
101+
try {
102+
await dbInstance.dropCollection(collectionName);
103+
} catch (e) {
104+
console.log(`Failed to drop collection: ${DB_NAME}.${collectionName}`);
105+
}
106+
}
107+
void mongoClient.close();
108+
await disconnect(browser);
109+
await afterTest(compass, this.currentTest);
110+
});
111+
112+
for (const { name, connectionString } of connectionsWithNoSearchSupport) {
113+
context(`does not support search indexes in ${name}`, function () {
114+
before(function () {
115+
if (!connectionString) {
116+
return this.skip();
117+
}
118+
currentConnectionString = connectionString;
119+
});
120+
121+
it('allows users to create a regular index', async function () {
122+
const indexName = await browser.createIndex({
123+
fieldName: 'e2e_tests_index',
124+
indexType: 'text',
125+
});
126+
await browser.dropIndex(indexName);
127+
});
128+
129+
it('renders search indexes tab disabled', async function () {
130+
const searchTab = await browser.$(
131+
Selectors.indexesSegmentedTab('search-indexes')
132+
);
133+
const isTabClickable = await searchTab.isClickable();
134+
expect(isTabClickable).to.be.false;
135+
});
136+
});
137+
}
138+
139+
for (const { name, connectionString } of connectionsWithSearchSupport) {
140+
context(`supports search indexes in ${name}`, function () {
141+
before(function () {
142+
if (!connectionString) {
143+
return this.skip();
144+
}
145+
currentConnectionString = connectionString;
146+
});
147+
148+
it('allows users to create a regular indexes', async function () {
149+
await browser.clickVisible(
150+
Selectors.indexesSegmentedTab('regular-indexes')
151+
);
152+
const indexName = await browser.createIndex({
153+
fieldName: 'e2e_tests_index',
154+
indexType: 'text',
155+
});
156+
await browser.dropIndex(indexName);
157+
});
158+
159+
it('allows users to create, view and drop search indexes', async function () {
160+
const indexName = `e2e_search_index_${getRandomNumber()}`;
161+
await browser.clickVisible(
162+
Selectors.indexesSegmentedTab('search-indexes')
163+
);
164+
await browser.createSearchIndex(indexName, INDEX_DEFINITION);
165+
await browser.waitForAnimations(Selectors.SearchIndexList);
166+
167+
const rowSelector = Selectors.searchIndexRow(indexName);
168+
169+
// View it
170+
await browser.$(rowSelector).waitForDisplayed();
171+
await browser.waitUntil(async function () {
172+
const text = await browser.$(rowSelector).getText();
173+
return text.indexOf('PENDING') > -1;
174+
});
175+
176+
// Drop it
177+
await browser.dropSearchIndex(indexName);
178+
// todo: check the index status to be either DELETING or the row disappears
179+
});
180+
181+
it('allows users to update and view search indexes');
182+
it('runs a search aggregation with index name');
183+
});
184+
}
185+
});

packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,12 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
164164
className={alignSelfEndStyles}
165165
label="Viewing"
166166
defaultValue="regular-indexes"
167+
data-testid="indexes-segment-controls"
167168
>
168-
<SegmentedControlOption value="regular-indexes">
169+
<SegmentedControlOption
170+
data-testid="regular-indexes-tab"
171+
value="regular-indexes"
172+
>
169173
Indexes
170174
</SegmentedControlOption>
171175
{!isAtlasSearchSupported && (
@@ -177,6 +181,7 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
177181
trigger={({ children, ...props }) => (
178182
<SegmentedControlOption
179183
{...props}
184+
data-testid="search-indexes-tab"
180185
value="search-indexes"
181186
disabled={true}
182187
>
@@ -189,7 +194,10 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
189194
</Tooltip>
190195
)}
191196
{isAtlasSearchSupported && (
192-
<SegmentedControlOption value="search-indexes">
197+
<SegmentedControlOption
198+
data-testid="search-indexes-tab"
199+
value="search-indexes"
200+
>
193201
Search Indexes
194202
</SegmentedControlOption>
195203
)}

0 commit comments

Comments
 (0)