Skip to content

Commit 33e2b44

Browse files
committed
page tests
1 parent 497e5b6 commit 33e2b44

File tree

6 files changed

+400
-189
lines changed

6 files changed

+400
-189
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { Page } from '@playwright/test';
2+
import { AdminPageBase } from '@models/AdminPageBase';
3+
4+
export interface ArticleTypeData {
5+
title: string;
6+
url?: string;
7+
seoTitle?: string;
8+
seoKeywords?: string;
9+
seoDescription?: string;
10+
status?: '有效' | '无效';
11+
description?: string;
12+
}
13+
14+
export class ArticleTypeFormPage extends AdminPageBase {
15+
// Form fields
16+
readonly titleField = this.page.locator('#Title');
17+
readonly urlField = this.page.locator('#Url');
18+
readonly seoTitleField = this.page.locator('#SEOTitle');
19+
readonly seoKeywordsField = this.page.locator('#SEOKeyWord');
20+
readonly seoDescriptionField = this.page.locator('#SEODescription');
21+
readonly statusField = this.page.locator('#Status');
22+
readonly descriptionField = this.page.locator('#Description');
23+
24+
// Buttons
25+
readonly saveButton = this.page.locator('input[data-value="Create"]');
26+
readonly saveAndExitButton = this.page.locator('input[data-value="CreateAndExit"]');
27+
readonly cancelButton = this.page.locator('a:has-text("取消")');
28+
readonly randomUrlButton = this.page.locator('.random');
29+
30+
// Form validation
31+
readonly titleValidation = this.page.locator('[data-valmsg-for="Title"]');
32+
readonly urlValidation = this.page.locator('[data-valmsg-for="Url"]');
33+
34+
constructor(page: Page) {
35+
super(page);
36+
}
37+
38+
/**
39+
* Navigate to the article type creation page
40+
*/
41+
async navigateTo(): Promise<void> {
42+
await this.page.goto('/admin/articletype/create');
43+
await this.waitForData();
44+
}
45+
46+
/**
47+
* Fill the article type form with provided data
48+
*/
49+
async fillArticleTypeForm(articleTypeData: ArticleTypeData): Promise<void> {
50+
// Fill required fields
51+
await this.titleField.fill(articleTypeData.title);
52+
53+
// Fill optional fields
54+
if (articleTypeData.url) {
55+
await this.urlField.fill(articleTypeData.url);
56+
} else {
57+
// Click the random URL button to generate one
58+
await this.randomUrlButton.click();
59+
}
60+
await this.fill(this.seoTitleField, articleTypeData.seoTitle);
61+
await this.fill(this.seoKeywordsField, articleTypeData.seoKeywords);
62+
await this.fill(this.seoDescriptionField, articleTypeData.seoDescription);
63+
await this.fill(this.statusField, articleTypeData.status);
64+
await this.fill(this.descriptionField, articleTypeData.description);
65+
}
66+
67+
/**
68+
* Submit the form and stay on the page
69+
*/
70+
async save(): Promise<void> {
71+
await this.saveButton.click();
72+
}
73+
74+
/**
75+
* Submit the form and return to the article type list
76+
*/
77+
async saveAndExit(): Promise<void> {
78+
await this.saveAndExitButton.click();
79+
}
80+
81+
/**
82+
* Cancel and return to the article type list
83+
*/
84+
async cancel(): Promise<void> {
85+
await this.cancelButton.click();
86+
}
87+
88+
/**
89+
* Create a new article type with the provided data
90+
*/
91+
async createArticleType(articleTypeData: ArticleTypeData): Promise<void> {
92+
await this.fillArticleTypeForm(articleTypeData);
93+
await this.save();
94+
}
95+
96+
/**
97+
* Wait for the page to be fully loaded
98+
*/
99+
async waitForData(): Promise<void> {
100+
await this.page.waitForSelector('#Title', { state: 'visible' });
101+
}
102+
103+
/**
104+
* Check if the form has validation errors
105+
*/
106+
async hasValidationErrors(): Promise<boolean> {
107+
const titleError = await this.titleValidation.isVisible();
108+
const urlError = await this.urlValidation.isVisible();
109+
return titleError || urlError;
110+
}
111+
112+
/**
113+
* Get validation error message for title field
114+
*/
115+
async getTitleErrorMessage(): Promise<string | null> {
116+
const isVisible = await this.titleValidation.isVisible();
117+
if (isVisible) {
118+
return await this.titleValidation.textContent();
119+
}
120+
return null;
121+
}
122+
123+
/**
124+
* Get validation error message for URL field
125+
*/
126+
async getUrlErrorMessage(): Promise<string | null> {
127+
const isVisible = await this.urlValidation.isVisible();
128+
if (isVisible) {
129+
return await this.urlValidation.textContent();
130+
}
131+
return null;
132+
}
133+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { AdminPageBase } from "@models/AdminPageBase";
2+
import { Page } from "@playwright/test";
3+
4+
export class ArticleTypeIndexPage extends AdminPageBase {
5+
6+
readonly createArticleTypeLink = this.page.locator('a[href="/admin/articletype/create"]');
7+
8+
constructor(page: Page) {
9+
super(page);
10+
}
11+
12+
async navigateTo(): Promise<void> {
13+
await this.page.goto('/admin/articletype');
14+
}
15+
16+
async goToCreateArticleTypePage(): Promise<void> {
17+
await this.createArticleTypeLink.click();
18+
}
19+
20+
async createSubArticleType(parentTypeName: string): Promise<void> {
21+
const parentTypeItem = await this.page.getByRole('treeitem', { name: parentTypeName, exact: true });
22+
await parentTypeItem.click({ button: 'right' });
23+
await this.page.locator('.jstree-contextmenu').locator('a[rel="0"]').click();
24+
}
25+
}

test/End-To-End/src/admin/PageFormPage.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export class PageFormPage extends AdminPageBase {
1919
await this.page.goto('/admin/page/create');
2020
}
2121

22+
async navigateToEditPage(pageId: string): Promise<void> {
23+
await this.page.goto(`/admin/page/edit/${pageId}`);
24+
}
25+
2226
async fillForm(data: PageFormData): Promise<void> {
2327
await this.fill(this.page.locator('input[name="PageName"]'), data.name);
2428
await this.fill(this.page.locator('input[name="Title"]'), data.title);
Lines changed: 96 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,119 @@
1-
import { test, expect } from '@playwright/test';
2-
import { ArticleFormPage } from '../../../src/admin/ArticleFormPage';
1+
import { test as base, expect } from '@playwright/test';
2+
import { ArticleFormPage } from '@admin/ArticleFormPage';
33

4-
test.describe('Article Form Page - Basic Functionality Tests', () => {
5-
let articleFormPage: ArticleFormPage;
6-
7-
test.beforeEach(async ({ page }) => {
8-
// Initialize the page object
9-
articleFormPage = new ArticleFormPage(page);
10-
11-
// Login before each test
4+
const test = base.extend<{ articleFormPage: ArticleFormPage }>({
5+
articleFormPage: async ({ page }, use) => {
6+
const articleFormPage = new ArticleFormPage(page);
127
await articleFormPage.login();
138
await articleFormPage.navigateTo();
9+
await use(articleFormPage);
10+
},
11+
});
12+
13+
test('should be able to create an article with required fields', async ({ articleFormPage, page }) => {
14+
15+
// Fill in the required fields
16+
const timestamp = new Date().getTime();
17+
await articleFormPage.fillArticleForm({
18+
title: 'Test Article ' + timestamp,
19+
url: 'test-article-' + timestamp,
20+
summary: 'This is a test article summary',
21+
content: 'This is the content of the test article.',
22+
articleType: '公司新闻' // News category
1423
});
1524

16-
test('should be able to create an article with required fields', async ({ page }) => {
17-
18-
// Fill in the required fields
19-
const timestamp = new Date().getTime();
20-
await articleFormPage.fillArticleForm({
21-
title: 'Test Article ' + timestamp,
22-
url: 'test-article-' + timestamp,
23-
summary: 'This is a test article summary',
24-
content: 'This is the content of the test article.',
25-
articleType: '公司新闻' // News category
26-
});
25+
// Save the article
26+
await articleFormPage.saveArticle();
2727

28-
// Save the article
29-
await articleFormPage.saveArticle();
28+
await expect(page).toHaveURL(/\/admin\/article\/edit\/\d+/);
29+
});
3030

31-
await expect(page).toHaveURL(/\/admin\/article\/edit\/\d+/);
32-
});
31+
test('should generate a random URL when the random button is clicked', async ({ articleFormPage, page }) => {
3332

34-
test('should generate a random URL when the random button is clicked', async ({ page }) => {
33+
// Initially the URL field should be empty
34+
await expect(articleFormPage.urlField).toBeEmpty();
3535

36-
// Initially the URL field should be empty
37-
await expect(articleFormPage.urlField).toBeEmpty();
36+
// Click the random URL button
37+
await articleFormPage.generateRandomUrl();
3838

39-
// Click the random URL button
40-
await articleFormPage.generateRandomUrl();
39+
// Check that the field now contains a value
40+
await expect(articleFormPage.urlField).not.toBeEmpty();
41+
});
4142

42-
// Check that the field now contains a value
43-
await expect(articleFormPage.urlField).not.toBeEmpty();
44-
});
43+
test('should be able to select an article type from the dropdown', async ({ articleFormPage, page }) => {
44+
// Select an article type
45+
await articleFormPage.selectArticleType('公司新闻');
4546

46-
test('should be able to select an article type from the dropdown', async ({ page }) => {
47-
// Select an article type
48-
await articleFormPage.selectArticleType('公司新闻');
47+
await expect(page.locator('#dropdown-tree-ArticleTypeID')).toContainText('公司新闻');
48+
});
4949

50-
// Check that the value has been set correctly
51-
const selectedArticleType = await articleFormPage.articleTypeField.inputValue();
52-
expect(selectedArticleType).not.toBe('');
50+
test('should be able to save and exit from the article form', async ({ articleFormPage, page }) => {
51+
// Fill in the required fields
52+
const timestamp = new Date().getTime();
53+
await articleFormPage.fillArticleForm({
54+
title: 'Test Article ' + timestamp,
55+
url: 'test-article-' + timestamp,
56+
summary: 'This is a test article summary',
57+
content: 'This is the content of the test article.',
58+
articleType: '公司新闻' // News category
5359
});
5460

55-
test('should be able to save and exit from the article form', async ({ page }) => {
56-
// Fill in the required fields
57-
const timestamp = new Date().getTime();
58-
await articleFormPage.fillArticleForm({
59-
title: 'Test Article ' + timestamp,
60-
url: 'test-article-' + timestamp,
61-
summary: 'This is a test article summary',
62-
content: 'This is the content of the test article.',
63-
articleType: '公司新闻' // News category
64-
});
65-
66-
// Save and exit
67-
await articleFormPage.saveAndExit();
68-
69-
// After saving and exiting, we should be redirected to the article list page
70-
await expect(page).toHaveURL(/\/admin\/article/);
71-
});
61+
// Save and exit
62+
await articleFormPage.saveAndExit();
7263

73-
test('should cancel and return to the article list page', async ({ page }) => {
64+
// After saving and exiting, we should be redirected to the article list page
65+
await expect(page).toHaveURL(/\/admin\/article/);
66+
});
7467

75-
// Click cancel
76-
await articleFormPage.cancel();
68+
test('should cancel and return to the article list page', async ({ articleFormPage, page }) => {
7769

78-
// Should be redirected to the article list page
79-
await expect(page).toHaveURL(/\/admin\/article/);
80-
});
70+
// Click cancel
71+
await articleFormPage.cancel();
72+
73+
// Should be redirected to the article list page
74+
await expect(page).toHaveURL(/\/admin\/article/);
75+
});
8176

8277

83-
test('should be able to save then publish article', async ({ page }) => {
84-
// Fill in the required fields
85-
const timestamp = new Date().getTime();
86-
await articleFormPage.fillArticleForm({
87-
title: 'Test Article ' + timestamp,
88-
url: 'test-article-' + timestamp,
89-
summary: 'This is a test article summary',
90-
content: 'This is the content of the test article.',
91-
articleType: '公司新闻' // News category
92-
});
93-
94-
// Save and exit
95-
await articleFormPage.saveArticle();
96-
page.on('dialog', async dialog => {
97-
await dialog.accept();
98-
});
99-
await articleFormPage.publishArticle();
100-
// After saving and exiting, we should be redirected to the article list page
101-
await expect(articleFormPage.publishDateField).not.toBeEmpty();
102-
await expect(page.getByText('已发布')).toBeVisible();
78+
test('should be able to save then publish article', async ({ articleFormPage, page }) => {
79+
// Fill in the required fields
80+
const timestamp = new Date().getTime();
81+
await articleFormPage.fillArticleForm({
82+
title: 'Test Article ' + timestamp,
83+
url: 'test-article-' + timestamp,
84+
summary: 'This is a test article summary',
85+
content: 'This is the content of the test article.',
86+
articleType: '公司新闻' // News category
10387
});
10488

105-
test('should be able to publish article directly', async ({ page }) => {
106-
// Fill in the required fields
107-
const timestamp = new Date().getTime();
108-
await articleFormPage.fillArticleForm({
109-
title: 'Test Article ' + timestamp,
110-
url: 'test-article-' + timestamp,
111-
summary: 'This is a test article summary',
112-
content: 'This is the content of the test article.',
113-
articleType: '公司新闻' // News category
114-
});
115-
page.on('dialog', async dialog => {
116-
await dialog.accept();
117-
});
118-
119-
// Publish directly
120-
await articleFormPage.publishArticle();
121-
// After publishing, we should see the publish date and status updated
122-
await expect(articleFormPage.publishDateField).not.toBeEmpty();
123-
await expect(page.getByText('已发布')).toBeVisible();
89+
// Save and exit
90+
await articleFormPage.saveArticle();
91+
page.on('dialog', async dialog => {
92+
await dialog.accept();
93+
});
94+
await articleFormPage.publishArticle();
95+
// After saving and exiting, we should be redirected to the article list page
96+
await expect(articleFormPage.publishDateField).not.toBeEmpty();
97+
await expect(page.getByText('已发布')).toBeVisible();
98+
});
99+
100+
test('should be able to publish article directly', async ({ articleFormPage, page }) => {
101+
// Fill in the required fields
102+
const timestamp = new Date().getTime();
103+
await articleFormPage.fillArticleForm({
104+
title: 'Test Article ' + timestamp,
105+
url: 'test-article-' + timestamp,
106+
summary: 'This is a test article summary',
107+
content: 'This is the content of the test article.',
108+
articleType: '公司新闻' // News category
124109
});
125-
});
110+
page.on('dialog', async dialog => {
111+
await dialog.accept();
112+
});
113+
114+
// Publish directly
115+
await articleFormPage.publishArticle();
116+
// After publishing, we should see the publish date and status updated
117+
await expect(articleFormPage.publishDateField).not.toBeEmpty();
118+
await expect(page.getByText('已发布')).toBeVisible();
119+
});

0 commit comments

Comments
 (0)