Skip to content

Commit d705d64

Browse files
authored
Refactor test (#239)
* chore: remove useless tests * chore: update test * chore: use page icon and name * chore: field type test * chore: add test * chore: fix test
1 parent 77f949d commit d705d64

23 files changed

+1440
-2957
lines changed

cypress/e2e/database/board-card-operations.cy.ts

Lines changed: 0 additions & 403 deletions
This file was deleted.
Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
/**
2+
* Board Operations E2E Tests
3+
*
4+
* Comprehensive tests for Board view functionality:
5+
* - Card operations (add, modify, delete)
6+
* - Card persistence and collaboration sync
7+
* - Consecutive board creation regression test
8+
*/
9+
import { v4 as uuidv4 } from 'uuid';
10+
11+
import { AuthTestUtils } from '../../support/auth-utils';
12+
import {
13+
AddPageSelectors,
14+
BoardSelectors,
15+
RowDetailSelectors,
16+
waitForReactUpdate,
17+
} from '../../support/selectors';
18+
19+
describe('Board Operations', () => {
20+
const generateRandomEmail = () => `${uuidv4()}@appflowy.io`;
21+
22+
beforeEach(() => {
23+
cy.on('uncaught:exception', (err) => {
24+
if (
25+
err.message.includes('Minified React error') ||
26+
err.message.includes('View not found') ||
27+
err.message.includes('No workspace or service found') ||
28+
err.message.includes('ResizeObserver loop')
29+
) {
30+
return false;
31+
}
32+
33+
return true;
34+
});
35+
36+
cy.viewport(1280, 720);
37+
});
38+
39+
/**
40+
* Helper: Create a Board and wait for it to load
41+
*/
42+
const createBoardAndWait = (authUtils: AuthTestUtils, testEmail: string) => {
43+
cy.visit('/login', { failOnStatusCode: false });
44+
cy.wait(2000);
45+
46+
return authUtils.signInWithTestUrl(testEmail).then(() => {
47+
cy.url({ timeout: 30000 }).should('include', '/app');
48+
cy.wait(3000);
49+
50+
// Create Board
51+
AddPageSelectors.inlineAddButton().first().click({ force: true });
52+
waitForReactUpdate(1000);
53+
cy.get('[role="menuitem"]').contains('Board').click({ force: true });
54+
cy.wait(5000);
55+
56+
// Verify Board loaded with default columns and cards
57+
BoardSelectors.boardContainer().should('exist', { timeout: 15000 });
58+
waitForReactUpdate(3000);
59+
BoardSelectors.cards().should('have.length.at.least', 1, { timeout: 15000 });
60+
BoardSelectors.boardContainer().contains('To Do').should('be.visible');
61+
BoardSelectors.boardContainer().contains('Doing').should('be.visible');
62+
BoardSelectors.boardContainer().contains('Done').should('be.visible');
63+
});
64+
};
65+
66+
describe('Board Creation', () => {
67+
/**
68+
* Regression test for blank page bug when creating second database.
69+
*/
70+
it('should display cards correctly when creating two Boards consecutively', () => {
71+
const testEmail = generateRandomEmail();
72+
73+
cy.task('log', `[TEST START] Create two Boards consecutively - Email: ${testEmail}`);
74+
75+
cy.visit('/login', { failOnStatusCode: false });
76+
cy.wait(2000);
77+
78+
const authUtils = new AuthTestUtils();
79+
authUtils.signInWithTestUrl(testEmail).then(() => {
80+
cy.url({ timeout: 30000 }).should('include', '/app');
81+
cy.wait(3000);
82+
83+
// ===== FIRST BOARD =====
84+
cy.task('log', '[STEP 1] Creating FIRST Board database');
85+
AddPageSelectors.inlineAddButton().first().click({ force: true });
86+
waitForReactUpdate(1000);
87+
cy.get('[role="menuitem"]').contains('Board').click({ force: true });
88+
cy.wait(5000);
89+
90+
BoardSelectors.boardContainer().should('exist', { timeout: 15000 });
91+
waitForReactUpdate(3000);
92+
BoardSelectors.cards().should('have.length.at.least', 1, { timeout: 15000 });
93+
BoardSelectors.boardContainer().contains('To Do').should('be.visible');
94+
95+
// ===== SECOND BOARD =====
96+
cy.task('log', '[STEP 2] Creating SECOND Board database');
97+
AddPageSelectors.inlineAddButton().first().click({ force: true });
98+
waitForReactUpdate(1000);
99+
cy.get('[role="menuitem"]').contains('Board').click({ force: true });
100+
cy.wait(5000);
101+
102+
// CRITICAL: Verify second Board loaded (not blank)
103+
BoardSelectors.boardContainer().should('exist', { timeout: 15000 });
104+
waitForReactUpdate(3000);
105+
BoardSelectors.cards().should('have.length.at.least', 1, { timeout: 15000 });
106+
BoardSelectors.boardContainer().contains('To Do').should('be.visible');
107+
BoardSelectors.boardContainer().contains('Doing').should('be.visible');
108+
BoardSelectors.boardContainer().contains('Done').should('be.visible');
109+
110+
cy.task('log', '[TEST COMPLETE] Both Boards created successfully');
111+
});
112+
});
113+
});
114+
115+
describe('Card Operations', () => {
116+
it('should add cards to different columns', () => {
117+
const testEmail = generateRandomEmail();
118+
const todoCard = `Todo-${uuidv4().substring(0, 6)}`;
119+
const doingCard = `Doing-${uuidv4().substring(0, 6)}`;
120+
const doneCard = `Done-${uuidv4().substring(0, 6)}`;
121+
122+
cy.task('log', `[TEST START] Add cards to different columns - Email: ${testEmail}`);
123+
124+
const authUtils = new AuthTestUtils();
125+
createBoardAndWait(authUtils, testEmail).then(() => {
126+
// Add card to "To Do"
127+
BoardSelectors.boardContainer()
128+
.contains('To Do')
129+
.closest('[data-column-id]')
130+
.within(() => {
131+
cy.contains('New').click({ force: true });
132+
});
133+
waitForReactUpdate(500);
134+
cy.focused().type(`${todoCard}{enter}`, { force: true });
135+
waitForReactUpdate(1500);
136+
137+
// Add card to "Doing"
138+
BoardSelectors.boardContainer()
139+
.contains('Doing')
140+
.closest('[data-column-id]')
141+
.within(() => {
142+
cy.contains('New').click({ force: true });
143+
});
144+
waitForReactUpdate(500);
145+
cy.focused().type(`${doingCard}{enter}`, { force: true });
146+
waitForReactUpdate(1500);
147+
148+
// Add card to "Done"
149+
BoardSelectors.boardContainer()
150+
.contains('Done')
151+
.closest('[data-column-id]')
152+
.within(() => {
153+
cy.contains('New').click({ force: true });
154+
});
155+
waitForReactUpdate(500);
156+
cy.focused().type(`${doneCard}{enter}`, { force: true });
157+
waitForReactUpdate(1500);
158+
159+
// Verify all cards
160+
BoardSelectors.boardContainer().contains(todoCard).should('be.visible');
161+
BoardSelectors.boardContainer().contains(doingCard).should('be.visible');
162+
BoardSelectors.boardContainer().contains(doneCard).should('be.visible');
163+
164+
cy.task('log', '[TEST COMPLETE] Add cards to different columns test passed');
165+
});
166+
});
167+
168+
it('should modify card title through detail view', () => {
169+
const testEmail = generateRandomEmail();
170+
const originalName = `Original-${uuidv4().substring(0, 6)}`;
171+
const modifiedName = `Modified-${uuidv4().substring(0, 6)}`;
172+
173+
cy.task('log', `[TEST START] Modify card title - Email: ${testEmail}`);
174+
175+
const authUtils = new AuthTestUtils();
176+
createBoardAndWait(authUtils, testEmail).then(() => {
177+
// Add a new card
178+
BoardSelectors.boardContainer().contains('New').first().click({ force: true });
179+
waitForReactUpdate(500);
180+
cy.focused().type(`${originalName}{enter}`, { force: true });
181+
waitForReactUpdate(2000);
182+
183+
// Open card detail modal
184+
BoardSelectors.boardContainer().contains(originalName).click({ force: true });
185+
waitForReactUpdate(1500);
186+
187+
// Verify modal opened
188+
cy.get('[role="dialog"]', { timeout: 10000 }).should('be.visible');
189+
cy.get('[role="dialog"]').contains(originalName, { timeout: 10000 }).should('be.visible');
190+
191+
// Modify title
192+
RowDetailSelectors.titleInput()
193+
.should('be.visible')
194+
.click({ force: true })
195+
.then(($input) => {
196+
cy.wrap($input).clear().type(modifiedName, { force: true });
197+
});
198+
199+
waitForReactUpdate(2000);
200+
201+
// Close modal
202+
cy.get('body').type('{esc}', { force: true });
203+
waitForReactUpdate(2000);
204+
205+
// Verify modified name
206+
BoardSelectors.boardContainer().contains(modifiedName, { timeout: 10000 }).should('be.visible');
207+
BoardSelectors.boardContainer().should('not.contain', originalName);
208+
209+
cy.task('log', '[TEST COMPLETE] Modify card title test passed');
210+
});
211+
});
212+
213+
it('should delete a card from the board', () => {
214+
const testEmail = generateRandomEmail();
215+
const cardToDelete = `DeleteMe-${uuidv4().substring(0, 6)}`;
216+
217+
cy.task('log', `[TEST START] Delete card - Email: ${testEmail}`);
218+
219+
const authUtils = new AuthTestUtils();
220+
createBoardAndWait(authUtils, testEmail).then(() => {
221+
// Add a new card
222+
BoardSelectors.boardContainer().contains('New').first().click({ force: true });
223+
waitForReactUpdate(500);
224+
cy.focused().type(`${cardToDelete}{enter}`, { force: true });
225+
waitForReactUpdate(2000);
226+
227+
BoardSelectors.boardContainer().contains(cardToDelete).should('be.visible');
228+
229+
// Hover to show toolbar
230+
BoardSelectors.boardContainer()
231+
.contains(cardToDelete)
232+
.closest('.board-card')
233+
.trigger('mouseenter', { force: true });
234+
waitForReactUpdate(500);
235+
236+
// Click more button
237+
BoardSelectors.boardContainer()
238+
.contains(cardToDelete)
239+
.closest('.board-card')
240+
.find('button')
241+
.last()
242+
.click({ force: true });
243+
waitForReactUpdate(500);
244+
245+
// Click delete
246+
cy.get('[role="menuitem"]').contains(/delete/i).click({ force: true });
247+
waitForReactUpdate(500);
248+
249+
// Confirm delete
250+
RowDetailSelectors.deleteRowConfirmButton().click({ force: true });
251+
waitForReactUpdate(2000);
252+
253+
// Verify deleted
254+
BoardSelectors.boardContainer().should('not.contain', cardToDelete, { timeout: 15000 });
255+
256+
cy.task('log', '[TEST COMPLETE] Delete card test passed');
257+
});
258+
});
259+
260+
it('should handle rapid card creation', () => {
261+
const testEmail = generateRandomEmail();
262+
const cardPrefix = `Rapid-${uuidv4().substring(0, 4)}`;
263+
const cardCount = 5;
264+
265+
cy.task('log', `[TEST START] Rapid card creation - Email: ${testEmail}`);
266+
267+
const authUtils = new AuthTestUtils();
268+
createBoardAndWait(authUtils, testEmail).then(() => {
269+
// Add multiple cards rapidly
270+
for (let i = 1; i <= cardCount; i++) {
271+
BoardSelectors.boardContainer().contains('New').first().click({ force: true });
272+
waitForReactUpdate(300);
273+
cy.focused().type(`${cardPrefix}-${i}{enter}`, { force: true });
274+
waitForReactUpdate(500);
275+
}
276+
277+
waitForReactUpdate(3000);
278+
279+
// Verify all cards
280+
for (let i = 1; i <= cardCount; i++) {
281+
BoardSelectors.boardContainer()
282+
.contains(`${cardPrefix}-${i}`, { timeout: 10000 })
283+
.should('be.visible');
284+
}
285+
286+
BoardSelectors.cards().should('have.length.at.least', cardCount);
287+
288+
cy.task('log', '[TEST COMPLETE] Rapid card creation test passed');
289+
});
290+
});
291+
});
292+
293+
describe('Card Persistence', () => {
294+
it('should persist card after page refresh', () => {
295+
const testEmail = generateRandomEmail();
296+
const persistentCard = `Persist-${uuidv4().substring(0, 6)}`;
297+
298+
cy.task('log', `[TEST START] Card persistence - Email: ${testEmail}`);
299+
300+
const authUtils = new AuthTestUtils();
301+
createBoardAndWait(authUtils, testEmail).then(() => {
302+
// Add card
303+
BoardSelectors.boardContainer().contains('New').first().click({ force: true });
304+
waitForReactUpdate(500);
305+
cy.focused().type(`${persistentCard}{enter}`, { force: true });
306+
waitForReactUpdate(2000);
307+
308+
BoardSelectors.boardContainer().contains(persistentCard).should('be.visible');
309+
310+
// Wait for sync and refresh
311+
waitForReactUpdate(3000);
312+
cy.reload();
313+
cy.wait(5000);
314+
315+
// Verify persisted
316+
BoardSelectors.boardContainer().should('exist', { timeout: 15000 });
317+
waitForReactUpdate(3000);
318+
BoardSelectors.boardContainer().contains(persistentCard, { timeout: 10000 }).should('be.visible');
319+
320+
cy.task('log', '[TEST COMPLETE] Card persistence test passed');
321+
});
322+
});
323+
324+
it('should sync new cards between collaborative sessions (iframe simulation)', () => {
325+
const testEmail = generateRandomEmail();
326+
const newCardName = `Collab-${uuidv4().substring(0, 6)}`;
327+
328+
cy.task('log', `[TEST START] Collaboration sync - Email: ${testEmail}`);
329+
330+
const authUtils = new AuthTestUtils();
331+
createBoardAndWait(authUtils, testEmail).then(() => {
332+
cy.url().then((currentUrl) => {
333+
// Add iframe with same page
334+
cy.document().then((doc) => {
335+
const iframe = doc.createElement('iframe');
336+
iframe.id = 'collab-iframe';
337+
iframe.src = currentUrl;
338+
iframe.style.cssText =
339+
'position: fixed; bottom: 0; right: 0; width: 600px; height: 400px; border: 2px solid blue; z-index: 9999;';
340+
doc.body.appendChild(iframe);
341+
});
342+
343+
cy.get('#collab-iframe', { timeout: 10000 }).should('exist');
344+
waitForReactUpdate(8000);
345+
346+
// Verify iframe loaded
347+
cy.get('#collab-iframe')
348+
.its('0.contentDocument.body')
349+
.find('.database-board', { timeout: 15000 })
350+
.should('exist');
351+
352+
// Add card in main window
353+
BoardSelectors.boardContainer().contains(/^\s*New\s*$/i).first().click({ force: true });
354+
waitForReactUpdate(1000);
355+
cy.focused().type(`${newCardName}{enter}`, { force: true });
356+
waitForReactUpdate(2000);
357+
358+
BoardSelectors.boardContainer().contains(newCardName).should('be.visible');
359+
360+
// Verify synced to iframe
361+
waitForReactUpdate(5000);
362+
cy.get('#collab-iframe')
363+
.its('0.contentDocument.body')
364+
.find('.database-board')
365+
.contains(newCardName, { timeout: 20000 })
366+
.should('exist');
367+
368+
// Cleanup
369+
cy.document().then((doc) => {
370+
const iframe = doc.getElementById('collab-iframe');
371+
if (iframe) iframe.remove();
372+
});
373+
374+
cy.task('log', '[TEST COMPLETE] Collaboration sync test passed');
375+
});
376+
});
377+
});
378+
});
379+
});

0 commit comments

Comments
 (0)