-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Expand file tree
/
Copy pathurl-navigation.spec.ts
More file actions
270 lines (254 loc) · 12.5 KB
/
url-navigation.spec.ts
File metadata and controls
270 lines (254 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
import { test, expect } from '@playwright/test';
import { register, generateUniqueUser, login } from './helpers/auth';
import { registerUserViaAPI, createManyArticles as createManyArticlesViaAPI } from './helpers/api';
import { createUserInIsolation, createManyArticles } from './helpers/setup';
import { API_MODE } from './helpers/config';
test.describe('URL-based Navigation (Realworld Issue #691)', () => {
test.afterEach(async ({ context }) => {
await context.close();
await new Promise(resolve => setTimeout(resolve, 1000));
});
test('/ should show Global Feed for everyone', async ({ page }) => {
await page.goto('/');
// Should see Global Feed active
await expect(page.locator('.nav-link:has-text("Global Feed")')).toHaveClass(/active/);
// Should see articles
await expect(page.locator('.article-preview').first()).toBeVisible({ timeout: 2000 });
// URL should be /
await expect(page).toHaveURL('/');
});
test('/?feed=following should show Your Feed (authenticated)', async ({ page }) => {
const user = generateUniqueUser();
await register(page, user.username, user.email, user.password);
await page.goto('/?feed=following');
// Should see Your Feed active
await expect(page.locator('.nav-link:has-text("Your Feed")')).toHaveClass(/active/);
// URL should have feed param
await expect(page).toHaveURL('/?feed=following');
});
test('/?feed=following should redirect to /login when not authenticated', async ({ page }) => {
await page.goto('/?feed=following');
// Should be redirected to login
await expect(page).toHaveURL('/login');
});
test('/tag/:tag should filter by tag', async ({ page }) => {
await page.goto('/');
await page.waitForSelector('.sidebar .tag-list', { timeout: 2000 });
// Get a tag from the sidebar
const firstTag = await page.locator('.sidebar .tag-list .tag-pill').first().textContent();
expect(firstTag).toBeTruthy();
// Navigate directly to the tag URL
await page.goto(`/tag/${firstTag?.trim()}`);
// Should see the tag filter active
await expect(page.locator(`.nav-link:has-text("${firstTag?.trim()}")`)).toBeVisible();
await expect(page.locator(`.nav-link:has-text("${firstTag?.trim()}")`)).toHaveClass(/active/);
});
test('tabs should have correct href attributes', async ({ page }) => {
const user = generateUniqueUser();
await register(page, user.username, user.email, user.password);
await page.goto('/');
await page.waitForSelector('.feed-toggle', { timeout: 2000 });
// Your Feed should link to /?feed=following
const yourFeedLink = page.locator('.nav-link:has-text("Your Feed")');
await expect(yourFeedLink).toHaveAttribute('href', '/?feed=following');
// Global Feed should link to /
const globalFeedLink = page.locator('.nav-link:has-text("Global Feed")');
await expect(globalFeedLink).toHaveAttribute('href', '/');
});
test('clicking Your Feed should navigate to /?feed=following', async ({ page }) => {
const user = generateUniqueUser();
await register(page, user.username, user.email, user.password);
// Should be at /
await expect(page).toHaveURL('/');
// Click Your Feed
await page.click('.nav-link:has-text("Your Feed")');
// Should navigate to /?feed=following
await expect(page).toHaveURL('/?feed=following');
await expect(page.locator('.nav-link:has-text("Your Feed")')).toHaveClass(/active/);
});
test('clicking Global Feed should navigate to /', async ({ page }) => {
const user = generateUniqueUser();
await register(page, user.username, user.email, user.password);
// Go to Your Feed first
await page.goto('/?feed=following');
await expect(page.locator('.nav-link:has-text("Your Feed")')).toHaveClass(/active/);
// Click Global Feed
await page.click('.nav-link:has-text("Global Feed")');
// Should navigate to /
await expect(page).toHaveURL('/');
await expect(page.locator('.nav-link:has-text("Global Feed")')).toHaveClass(/active/);
});
test('empty Your Feed shows helpful message with link to Global Feed', async ({ page }) => {
const user = generateUniqueUser();
await register(page, user.username, user.email, user.password);
await page.goto('/?feed=following');
// Wait for loading to complete
await page.waitForSelector('.empty-feed-message', { timeout: 2000 });
// Should show helpful empty message
const emptyMessage = page.locator('.empty-feed-message');
await expect(emptyMessage).toContainText('Your feed is empty');
// Should have a link to Global Feed
const globalFeedLink = emptyMessage.locator('a[href="/"]');
await expect(globalFeedLink).toBeVisible();
});
});
test.describe('Pagination', () => {
test.afterEach(async ({ context }) => {
await context.close();
await new Promise(resolve => setTimeout(resolve, 1000));
});
test('pagination should update URL with ?page=N', async ({ page, request, browser }) => {
// Create user and 15 articles with a unique tag for this test
const uniqueTag = `pag${Date.now()}`;
const testUser = generateUniqueUser();
if (API_MODE) {
const token = await registerUserViaAPI(request, testUser);
await createManyArticlesViaAPI(request, token, 15, uniqueTag);
await login(page, testUser.email, testUser.password);
} else {
await createUserInIsolation(browser, testUser);
await login(page, testUser.email, testUser.password);
await createManyArticles(page, 15, uniqueTag);
}
// Navigate to the tag page - this shows ONLY our articles
await page.goto(`/tag/${uniqueTag}`);
await page.waitForSelector('.article-preview', { timeout: 2000 });
// Should have pagination (15 articles = 2 pages with limit 10)
await expect(page.locator('.pagination button:has-text("2")')).toBeVisible({ timeout: 2000 });
// Click page 2
await page.click('.pagination button:has-text("2")');
// URL should have ?page=2
await expect(page).toHaveURL(new RegExp(`/tag/${uniqueTag}\\?page=2`));
// Page 2 should be active
await expect(page.locator('.pagination .page-item:has(button:has-text("2"))')).toHaveClass(/active/);
});
test('should load correct page when navigating directly to ?page=N', async ({ page, request, browser }) => {
// Create user and 15 articles with a unique tag for this test
const uniqueTag = `pag${Date.now()}`;
const testUser = generateUniqueUser();
if (API_MODE) {
const token = await registerUserViaAPI(request, testUser);
await createManyArticlesViaAPI(request, token, 15, uniqueTag);
await login(page, testUser.email, testUser.password);
} else {
await createUserInIsolation(browser, testUser);
await login(page, testUser.email, testUser.password);
await createManyArticles(page, 15, uniqueTag);
}
// Go directly to page 2 of the tag
await page.goto(`/tag/${uniqueTag}?page=2`);
await page.waitForSelector('.article-preview', { timeout: 2000 });
// Page 2 should be active
await expect(page.locator('.pagination .page-item:has(button:has-text("2"))')).toHaveClass(/active/);
// URL should have page=2
const url = new URL(page.url());
expect(url.searchParams.get('page')).toBe('2');
});
test('pagination URL preserves feed=following parameter', async ({ page }) => {
const user = generateUniqueUser();
await register(page, user.username, user.email, user.password);
// Your Feed shows articles from users you FOLLOW (not your own articles)
// Just verify that if pagination exists on Your Feed, the URL is correct
await page.goto('/?feed=following');
// Wait for the page to load (might be empty or have articles)
await page.waitForSelector('.article-preview, .empty-feed-message', { timeout: 2000 });
// Check if pagination exists (depends on followed users having 11+ articles)
const page2Button = page.locator('.pagination button:has-text("2")');
const hasPage2 = await page2Button.isVisible().catch(() => false);
if (hasPage2) {
// Click page 2
await page.click('.pagination button:has-text("2")');
// URL should preserve feed param and add page
await expect(page).toHaveURL('/?feed=following&page=2');
} else {
// No pagination available - just verify URL structure is correct
await expect(page).toHaveURL('/?feed=following');
}
});
test('pagination should work with /tag/:tag', async ({ page, request, browser }) => {
// Create user and 15 articles with a unique tag for this test
const uniqueTag = `pag${Date.now()}`;
const testUser = generateUniqueUser();
if (API_MODE) {
const token = await registerUserViaAPI(request, testUser);
await createManyArticlesViaAPI(request, token, 15, uniqueTag);
await login(page, testUser.email, testUser.password);
} else {
await createUserInIsolation(browser, testUser);
await login(page, testUser.email, testUser.password);
await createManyArticles(page, 15, uniqueTag);
}
// Navigate to our tag
await page.goto(`/tag/${uniqueTag}`);
await page.waitForSelector('.article-preview', { timeout: 2000 });
// Should have pagination
await expect(page.locator('.pagination button:has-text("2")')).toBeVisible({ timeout: 2000 });
// Click page 2
await page.click('.pagination button:has-text("2")');
// Wait for URL to update after page navigation
await expect(page).toHaveURL(`/tag/${uniqueTag}?page=2`);
});
test('page should reset when switching feeds', async ({ page, request, browser }) => {
// Create user and 15 articles with a unique tag for this test
const uniqueTag = `pag${Date.now()}`;
const testUser = generateUniqueUser();
if (API_MODE) {
const token = await registerUserViaAPI(request, testUser);
await createManyArticlesViaAPI(request, token, 15, uniqueTag);
await login(page, testUser.email, testUser.password);
} else {
await createUserInIsolation(browser, testUser);
await login(page, testUser.email, testUser.password);
await createManyArticles(page, 15, uniqueTag);
}
// Navigate to our tag
await page.goto(`/tag/${uniqueTag}`);
await page.waitForSelector('.article-preview', { timeout: 2000 });
// Should have pagination
await expect(page.locator('.pagination button:has-text("2")')).toBeVisible({ timeout: 2000 });
// Go to page 2
await page.click('.pagination button:has-text("2")');
await expect(page).toHaveURL(new RegExp(`/tag/${uniqueTag}\\?page=2`));
// Click Global Feed and wait for URL to change to root path
await page.click('.nav-link:has-text("Global Feed")');
await expect(page).toHaveURL('/');
// Verify articles loaded
await page.waitForSelector('.article-preview', { timeout: 2000 });
});
test('tag pagination shows correct articles per page', async ({ page, request, browser }) => {
// Create user and 15 articles with a unique tag for this test
const uniqueTag = `pag${Date.now()}`;
const testUser = generateUniqueUser();
if (API_MODE) {
const token = await registerUserViaAPI(request, testUser);
await createManyArticlesViaAPI(request, token, 15, uniqueTag);
await login(page, testUser.email, testUser.password);
} else {
await createUserInIsolation(browser, testUser);
await login(page, testUser.email, testUser.password);
await createManyArticles(page, 15, uniqueTag);
}
// Navigate to our tag
await page.goto(`/tag/${uniqueTag}`);
await page.waitForSelector('.article-preview', { timeout: 2000 });
// Should have pagination (15 articles = 2 pages)
await expect(page.locator('.pagination button:has-text("2")')).toBeVisible({ timeout: 2000 });
// First page should show 10 articles
const articlesOnPage1 = await page.locator('.article-preview').count();
expect(articlesOnPage1).toBe(10);
// Click page 2
await page.click('.pagination button:has-text("2")');
// Wait for page 2 to be active (Angular routing/rendering delay)
await expect(page.locator('.pagination .page-item:has(button:has-text("2"))')).toHaveClass(/active/, {
timeout: 2000,
});
await page.waitForSelector('.article-preview', { timeout: 2000 });
// URL should show ?page=2
await expect(page).toHaveURL(new RegExp(`/tag/${uniqueTag}\\?page=2`));
// Small wait for Angular to finish rendering the new page
await page.waitForTimeout(500);
// Second page should have 5 articles (15 - 10 = 5)
const articlesOnPage2 = await page.locator('.article-preview').count();
expect(articlesOnPage2).toBe(5);
});
});