Skip to content

Commit 4840ff0

Browse files
committed
Add E2E tests for Member Clusters in the dashboard
Signed-off-by: SunsetB612 <[email protected]>
1 parent aa6df58 commit 4840ff0

File tree

4 files changed

+471
-0
lines changed

4 files changed

+471
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
Copyright 2025 The Karmada Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
import { test, expect } from '@playwright/test';
17+
import { setupDashboardAuthentication, generateTestMemberClusterResourceYaml, createK8sMemberCluster, getMemberClusterNameFromResourceYaml, deleteK8sMemberCluster} from './test-utils';
18+
19+
test.beforeEach(async ({ page }) => {
20+
await setupDashboardAuthentication(page);
21+
});
22+
23+
test('should delete member cluster successfully', async ({ page }) => {
24+
// Create a test member cluster directly via API to set up test data
25+
const testMemberClusterYaml = generateTestMemberClusterResourceYaml();
26+
const memberClusterName = getMemberClusterNameFromResourceYaml(testMemberClusterYaml);
27+
28+
// Setup: Create member cluster using API
29+
await createK8sMemberCluster(testMemberClusterYaml);
30+
31+
// Open Member Clusters menu
32+
await page.click('text=Member Clusters');
33+
34+
// Verify table is visible
35+
await expect(page.locator('table')).toBeVisible({ timeout: 30000 });
36+
37+
// Wait for member cluster to appear in list
38+
const table = page.locator('table');
39+
await expect(table.locator(`text=${memberClusterName}`)).toBeVisible({ timeout: 30000 });
40+
41+
// Find row containing test member cluster name
42+
const targetRow = page.locator(`table tbody tr:has-text("${memberClusterName}")`);
43+
await expect(targetRow).toBeVisible({ timeout: 15000 });
44+
45+
// Find Delete button in that row and click
46+
const deleteButton = targetRow.locator('button[type="button"]').filter({ hasText: /^(Delete)$/ });
47+
await expect(deleteButton).toBeVisible({ timeout: 10000 });
48+
49+
// Listen for delete API call
50+
const deleteApiPromise = page.waitForResponse(response => {
51+
return response.url().includes('/cluster') &&
52+
response.request().method() === 'DELETE' &&
53+
response.status() === 200;
54+
}, { timeout: 15000 });
55+
56+
await deleteButton.click();
57+
58+
// Wait for delete confirmation tooltip to appear
59+
await page.waitForSelector('[role="tooltip"]', { timeout: 10000 });
60+
61+
// Click Confirm button to confirm deletion
62+
const confirmButton = page.locator('[role="tooltip"] button').filter({ hasText: /^(\s*|Confirm)$/ });
63+
await expect(confirmButton).toBeVisible({ timeout: 5000 });
64+
await confirmButton.click();
65+
66+
// Wait for delete API call to succeed
67+
await deleteApiPromise;
68+
69+
// Wait for tooltip to close
70+
await page.waitForSelector('[role="tooltip"]', { state: 'detached', timeout: 10000 }).catch(() => {});
71+
72+
// Refresh page to ensure UI is updated after deletion
73+
await page.reload();
74+
await page.click('text=Member Clusters');
75+
await expect(table).toBeVisible({ timeout: 30000 });
76+
77+
// Verify member cluster no longer exists in table
78+
await expect(table.locator(`text=${memberClusterName}`)).toHaveCount(0, { timeout: 30000 });
79+
80+
// Cleanup: Ensure member cluster is deleted
81+
try {
82+
await deleteK8sMemberCluster(memberClusterName);
83+
} catch (error) {
84+
// Ignore cleanup errors as the resource might already be deleted
85+
}
86+
87+
// Debug
88+
if(process.env.DEBUG === 'true'){
89+
await page.screenshot({ path: 'debug-membercluster-delete.png', fullPage: true });
90+
}
91+
});
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
Copyright 2025 The Karmada Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { test, expect } from '@playwright/test';
18+
import { setupDashboardAuthentication, generateTestMemberClusterResourceYaml, createK8sMemberCluster, getMemberClusterNameFromResourceYaml, deleteK8sMemberCluster } from './test-utils';
19+
20+
test.beforeEach(async ({ page }) => {
21+
await setupDashboardAuthentication(page);
22+
});
23+
24+
test('should edit member cluster successfully', async ({ page }) => {
25+
// Create a test member cluster directly via API to set up test data
26+
const testMemberClusterYaml = generateTestMemberClusterResourceYaml();
27+
const memberClusterName = getMemberClusterNameFromResourceYaml(testMemberClusterYaml);
28+
29+
// Setup: Create member cluster using API
30+
await createK8sMemberCluster(testMemberClusterYaml);
31+
32+
// Open Member Clusters menu
33+
await page.click('text=Member Clusters');
34+
35+
// Verify table is visible
36+
await expect(page.locator('table')).toBeVisible({ timeout: 30000 });
37+
38+
// Wait for member cluster to appear in list
39+
const table = page.locator('table');
40+
await expect(table.locator(`text=${memberClusterName}`)).toBeVisible({ timeout: 30000 });
41+
42+
// Find row containing test member cluster name
43+
const targetRow = page.locator(`table tbody tr:has-text("${memberClusterName}")`);
44+
await expect(targetRow).toBeVisible({ timeout: 15000 });
45+
46+
// Find Edit button in that row and click
47+
const editButton = targetRow.getByText('Edit');
48+
await expect(editButton).toBeVisible({ timeout: 15000 });
49+
50+
// Listen for update API call
51+
const updateApiPromise = page.waitForResponse(response => {
52+
return response.url().includes(`/cluster/${memberClusterName}`) &&
53+
response.request().method() === 'PUT' &&
54+
response.status() === 200;
55+
}, { timeout: 15000 });
56+
57+
await editButton.click();
58+
59+
// Wait for edit dialog to appear
60+
await expect(page.locator('[role="dialog"]')).toBeVisible({ timeout: 10000 });
61+
62+
// Verify the dialog title
63+
await expect(page.locator('text=Edit Cluster')).toBeVisible();
64+
65+
// Verify name field is populated and disabled
66+
const nameField = page.getByRole('textbox', { name: '* Name :' });
67+
await expect(nameField).toHaveValue(memberClusterName);
68+
69+
// Modify cluster taint
70+
const taintKeyField = page.getByRole('textbox', { name: 'Please output the key of the point' });
71+
const taintValueField = page.getByRole('textbox', { name: 'Please output the value of the point' });
72+
73+
if (await taintKeyField.isVisible() && await taintValueField.isVisible()) {
74+
try {
75+
await taintKeyField.clear();
76+
await taintKeyField.fill('test-key');
77+
await taintValueField.fill('test-value');
78+
} catch (error) {
79+
// Continue with form submission if taint modification fails
80+
}
81+
}
82+
83+
// Click Submit button
84+
const submitButton = page.locator('[role="dialog"] button:has-text("Submit")');
85+
await expect(submitButton).toBeVisible({ timeout: 5000 });
86+
await submitButton.click();
87+
88+
// Wait for the update API call to complete
89+
await updateApiPromise;
90+
91+
// Cleanup: Delete the created member cluster
92+
try {
93+
await deleteK8sMemberCluster(memberClusterName);
94+
} catch (error) {
95+
console.warn(`Failed to cleanup member cluster ${memberClusterName}:`, error);
96+
}
97+
98+
// Debug
99+
if (process.env.DEBUG === 'true') {
100+
await page.screenshot({ path: 'debug-membercluster-edit.png', fullPage: true });
101+
}
102+
103+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
Copyright 2025 The Karmada Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// apps/dashboard/e2e/member-cluster-list.spec.ts
18+
import { test, expect } from '@playwright/test';
19+
import { setupDashboardAuthentication } from './test-utils';
20+
21+
test.beforeEach(async ({ page }) => {
22+
await setupDashboardAuthentication(page);
23+
});
24+
25+
test('should display member cluster list', async ({ page }) => {
26+
// Open Member Clusters menu
27+
await page.click('text=Member Clusters');
28+
29+
// // Click Member Clusters menu item
30+
// const memberClustersMenuItem = page.locator('text=Member Clusters');
31+
// await memberClustersMenuItem.waitFor({ state: 'visible', timeout: 30000 });
32+
// await memberClustersMenuItem.click();
33+
34+
// Verify Member Clusters list table is visible
35+
const table = page.locator('table');
36+
await expect(table).toBeVisible({ timeout: 30000 });
37+
38+
// Verify table headers for member clusters
39+
await expect(page.locator('th:has-text("Name")')).toBeVisible();
40+
await expect(page.locator('th:has-text("Kubernetes version")')).toBeVisible();
41+
await expect(page.locator('th:has-text("Cluster Status")')).toBeVisible();
42+
await expect(page.locator('th:has-text("Access Mode")')).toBeVisible();
43+
await expect(page.locator('th:has-text("Node Status")')).toBeVisible();
44+
45+
// Debug
46+
if (process.env.DEBUG === 'true') {
47+
await page.screenshot({ path: 'debug-member-cluster-list.png', fullPage: true });
48+
}
49+
});

0 commit comments

Comments
 (0)