Skip to content

Commit b28b884

Browse files
authored
test: add E2E tests for Consumer Groups (#3086) (#3250)
* test: add E2E tests for Consumer Groups (#3086) - Add POM file for consumer groups navigation - Add deleteAllConsumerGroups API function - Add list page test with pagination - Add CRUD test with required fields (id + plugins) - Add CRUD test with all fields including description and basic-auth plugin - All 5 tests passing * test(e2e): create consumer group via UI instead of API - Update required-fields test to create consumer group through UI - Add minimal plugin configuration (basic-auth) required by UI - Remove API-based creation in favor of full UI workflow
1 parent 2d7e194 commit b28b884

File tree

5 files changed

+487
-1
lines changed

5 files changed

+487
-1
lines changed

e2e/pom/consumer_groups.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { uiGoto } from '@e2e/utils/ui';
18+
import { expect, type Page } from '@playwright/test';
19+
20+
const locator = {
21+
getConsumerGroupNavBtn: (page: Page) =>
22+
page.getByRole('link', { name: 'Consumer Groups' }),
23+
getAddConsumerGroupBtn: (page: Page) =>
24+
page.getByRole('button', { name: 'Add Consumer Group' }),
25+
getAddBtn: (page: Page) =>
26+
page.getByRole('button', { name: 'Add', exact: true }),
27+
};
28+
29+
const assert = {
30+
isIndexPage: async (page: Page) => {
31+
await expect(page).toHaveURL((url) =>
32+
url.pathname.endsWith('/consumer_groups')
33+
);
34+
const title = page.getByRole('heading', { name: 'Consumer Groups' });
35+
await expect(title).toBeVisible();
36+
},
37+
isAddPage: async (page: Page) => {
38+
await expect(page).toHaveURL((url) =>
39+
url.pathname.endsWith('/consumer_groups/add')
40+
);
41+
const title = page.getByRole('heading', { name: 'Add Consumer Group' });
42+
await expect(title).toBeVisible();
43+
},
44+
isDetailPage: async (page: Page) => {
45+
await expect(page).toHaveURL((url) =>
46+
url.pathname.includes('/consumer_groups/detail')
47+
);
48+
const title = page.getByRole('heading', { name: 'Consumer Group Detail' });
49+
await expect(title).toBeVisible();
50+
},
51+
};
52+
53+
const goto = {
54+
toIndex: (page: Page) => uiGoto(page, '/consumer_groups'),
55+
toAdd: (page: Page) => uiGoto(page, '/consumer_groups/add'),
56+
};
57+
58+
export const consumerGroupsPom = {
59+
...locator,
60+
...assert,
61+
...goto,
62+
};
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { consumerGroupsPom } from '@e2e/pom/consumer_groups';
18+
import { e2eReq } from '@e2e/utils/req';
19+
import { test } from '@e2e/utils/test';
20+
import {
21+
uiFillMonacoEditor,
22+
uiGetMonacoEditor,
23+
uiHasToastMsg,
24+
} from '@e2e/utils/ui';
25+
import { expect } from '@playwright/test';
26+
27+
import { deleteAllConsumerGroups } from '@/apis/consumer_groups';
28+
29+
test.beforeAll(async () => {
30+
await deleteAllConsumerGroups(e2eReq);
31+
});
32+
33+
test('should CRUD Consumer Group with all fields', async ({ page }) => {
34+
test.slow();
35+
36+
const testId = 'test-consumer-group-all-fields';
37+
const testDesc = 'Consumer Group with all fields for testing';
38+
39+
await consumerGroupsPom.toIndex(page);
40+
await consumerGroupsPom.isIndexPage(page);
41+
42+
await test.step('create consumer group with all fields', async () => {
43+
// Navigate to add page
44+
await consumerGroupsPom.getAddConsumerGroupBtn(page).click();
45+
await consumerGroupsPom.isAddPage(page);
46+
47+
// Fill in the ID
48+
const idField = page.getByRole('textbox', { name: 'ID', exact: true });
49+
await idField.clear();
50+
await idField.fill(testId);
51+
52+
// Fill in the description
53+
const descField = page.getByRole('textbox', { name: 'Description' });
54+
await descField.fill(testDesc);
55+
56+
// Add plugins
57+
const selectPluginsBtn = page.getByRole('button', {
58+
name: 'Select Plugins',
59+
});
60+
await selectPluginsBtn.click();
61+
62+
// Add basic-auth plugin (simpler than limit-count which requires redis)
63+
const selectPluginsDialog = page.getByRole('dialog', {
64+
name: 'Select Plugins',
65+
});
66+
const searchInput = selectPluginsDialog.getByPlaceholder('Search');
67+
await searchInput.fill('basic-auth');
68+
69+
await selectPluginsDialog
70+
.getByTestId('plugin-basic-auth')
71+
.getByRole('button', { name: 'Add' })
72+
.click();
73+
74+
const addPluginDialog = page.getByRole('dialog', { name: 'Add Plugin' });
75+
const pluginEditor = await uiGetMonacoEditor(page, addPluginDialog);
76+
await uiFillMonacoEditor(
77+
page,
78+
pluginEditor,
79+
'{"hide_credentials": true}'
80+
);
81+
// add plugin
82+
await addPluginDialog.getByRole('button', { name: 'Add' }).click();
83+
await expect(addPluginDialog).toBeHidden();
84+
85+
// Submit the form
86+
await consumerGroupsPom.getAddBtn(page).click();
87+
await uiHasToastMsg(page, {
88+
hasText: 'Add Consumer Group Successfully',
89+
});
90+
91+
// Should navigate to detail page
92+
await consumerGroupsPom.isDetailPage(page);
93+
});
94+
95+
await test.step('verify all fields in detail page', async () => {
96+
// Verify ID
97+
const idField = page.getByRole('textbox', { name: 'ID', exact: true });
98+
await expect(idField).toHaveValue(testId);
99+
await expect(idField).toBeDisabled();
100+
101+
// Verify description
102+
const descField = page.getByRole('textbox', { name: 'Description' });
103+
await expect(descField).toHaveValue(testDesc);
104+
await expect(descField).toBeDisabled();
105+
106+
// Verify plugin is added
107+
await expect(page.getByText('basic-auth')).toBeVisible();
108+
});
109+
110+
await test.step('edit and update consumer group', async () => {
111+
// Click Edit button
112+
await page.getByRole('button', { name: 'Edit' }).click();
113+
114+
// Verify we're in edit mode
115+
const descField = page.getByRole('textbox', { name: 'Description' });
116+
await expect(descField).toBeEnabled();
117+
118+
// Update description
119+
await descField.clear();
120+
await descField.fill('Updated description with all fields');
121+
122+
// Save changes
123+
await page.getByRole('button', { name: 'Save', exact: true }).click();
124+
await uiHasToastMsg(page, {
125+
hasText: 'success',
126+
});
127+
128+
// Verify we're back in detail view
129+
await consumerGroupsPom.isDetailPage(page);
130+
131+
// Verify updated description
132+
await expect(descField).toHaveValue('Updated description with all fields');
133+
await expect(descField).toBeDisabled();
134+
});
135+
136+
await test.step('verify consumer group exists in list', async () => {
137+
// Navigate to list page
138+
await consumerGroupsPom.getConsumerGroupNavBtn(page).click();
139+
await consumerGroupsPom.isIndexPage(page);
140+
141+
// Verify consumer group exists
142+
await expect(page.getByRole('cell', { name: testId, exact: true })).toBeVisible();
143+
await expect(
144+
page.getByRole('cell', { name: 'Updated description with all fields' })
145+
).toBeVisible();
146+
});
147+
148+
await test.step('delete consumer group', async () => {
149+
// Click View to go to detail page
150+
await page
151+
.getByRole('row', { name: new RegExp(testId) })
152+
.getByRole('button', { name: 'View' })
153+
.click();
154+
await consumerGroupsPom.isDetailPage(page);
155+
156+
// Delete consumer group
157+
await page.getByRole('button', { name: 'Delete' }).click();
158+
159+
await page
160+
.getByRole('dialog', { name: 'Delete Consumer Group' })
161+
.getByRole('button', { name: 'Delete' })
162+
.click();
163+
164+
// Should redirect to list page
165+
await consumerGroupsPom.isIndexPage(page);
166+
await uiHasToastMsg(page, {
167+
hasText: 'Delete Consumer Group Successfully',
168+
});
169+
170+
// Verify deletion
171+
await expect(page.getByRole('cell', { name: testId, exact: true })).toBeHidden();
172+
});
173+
});
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { consumerGroupsPom } from '@e2e/pom/consumer_groups';
18+
import { e2eReq } from '@e2e/utils/req';
19+
import { test } from '@e2e/utils/test';
20+
import {
21+
uiFillMonacoEditor,
22+
uiGetMonacoEditor,
23+
uiHasToastMsg,
24+
} from '@e2e/utils/ui';
25+
import { expect } from '@playwright/test';
26+
27+
import { deleteAllConsumerGroups } from '@/apis/consumer_groups';
28+
29+
test.beforeAll(async () => {
30+
await deleteAllConsumerGroups(e2eReq);
31+
});
32+
33+
test('should CRUD Consumer Group with required fields', async ({ page }) => {
34+
const testId = 'test-consumer-group-required';
35+
36+
await consumerGroupsPom.toIndex(page);
37+
await consumerGroupsPom.isIndexPage(page);
38+
39+
await test.step('create consumer group with required fields', async () => {
40+
// Navigate to add page
41+
await consumerGroupsPom.getAddConsumerGroupBtn(page).click();
42+
await consumerGroupsPom.isAddPage(page);
43+
44+
// Fill in the ID
45+
const idField = page.getByRole('textbox', { name: 'ID', exact: true });
46+
await idField.clear();
47+
await idField.fill(testId);
48+
49+
// Add a minimal plugin (UI requires at least one plugin)
50+
const selectPluginsBtn = page.getByRole('button', {
51+
name: 'Select Plugins',
52+
});
53+
await selectPluginsBtn.click();
54+
55+
const selectPluginsDialog = page.getByRole('dialog', {
56+
name: 'Select Plugins',
57+
});
58+
const searchInput = selectPluginsDialog.getByPlaceholder('Search');
59+
await searchInput.fill('basic-auth');
60+
61+
await selectPluginsDialog
62+
.getByTestId('plugin-basic-auth')
63+
.getByRole('button', { name: 'Add' })
64+
.click();
65+
66+
const addPluginDialog = page.getByRole('dialog', { name: 'Add Plugin' });
67+
const pluginEditor = await uiGetMonacoEditor(page, addPluginDialog);
68+
await uiFillMonacoEditor(page, pluginEditor, '{"hide_credentials": true}');
69+
70+
await addPluginDialog.getByRole('button', { name: 'Add' }).click();
71+
await expect(addPluginDialog).toBeHidden();
72+
73+
// Submit the form
74+
await consumerGroupsPom.getAddBtn(page).click();
75+
await uiHasToastMsg(page, {
76+
hasText: 'Add Consumer Group Successfully',
77+
});
78+
79+
// Should navigate to detail page
80+
await consumerGroupsPom.isDetailPage(page);
81+
});
82+
83+
await test.step('verify consumer group in detail page', async () => {
84+
// Verify the ID
85+
const idField = page.getByRole('textbox', { name: 'ID', exact: true });
86+
await expect(idField).toHaveValue(testId);
87+
await expect(idField).toBeDisabled();
88+
});
89+
90+
await test.step('verify consumer group exists in list', async () => {
91+
// Navigate to list page
92+
await consumerGroupsPom.getConsumerGroupNavBtn(page).click();
93+
await consumerGroupsPom.isIndexPage(page);
94+
95+
// Verify consumer group exists in list
96+
await expect(
97+
page.getByRole('cell', { name: testId, exact: true })
98+
).toBeVisible();
99+
});
100+
101+
await test.step('navigate to detail and verify can edit', async () => {
102+
// Click View button to go to detail page
103+
await page
104+
.getByRole('row', { name: new RegExp(testId) })
105+
.getByRole('button', { name: 'View' })
106+
.click();
107+
await consumerGroupsPom.isDetailPage(page);
108+
109+
// Click Edit button
110+
await page.getByRole('button', { name: 'Edit' }).click();
111+
112+
// Verify we're in edit mode
113+
const descField = page.getByRole('textbox', { name: 'Description' });
114+
await expect(descField).toBeEnabled();
115+
116+
// Verify ID field is still disabled even in edit mode
117+
const idField = page.getByRole('textbox', { name: 'ID', exact: true });
118+
await expect(idField).toBeDisabled();
119+
120+
// Cancel without making changes
121+
await page.getByRole('button', { name: 'Cancel' }).click();
122+
123+
// Verify we're back in detail view
124+
await consumerGroupsPom.isDetailPage(page);
125+
await expect(descField).toBeDisabled();
126+
});
127+
128+
await test.step('delete consumer group', async () => {
129+
// Click Delete button
130+
await page.getByRole('button', { name: 'Delete' }).click();
131+
132+
// Confirm deletion in dialog
133+
await page
134+
.getByRole('dialog', { name: 'Delete Consumer Group' })
135+
.getByRole('button', { name: 'Delete' })
136+
.click();
137+
138+
// Should redirect to list page
139+
await consumerGroupsPom.isIndexPage(page);
140+
141+
// Verify consumer group is deleted
142+
await expect(
143+
page.getByRole('cell', { name: testId, exact: true })
144+
).toBeHidden();
145+
});
146+
});
147+

0 commit comments

Comments
 (0)