Skip to content

Commit 88cfcb0

Browse files
authored
chore: disable move linked view (#206)
1 parent 26e2d9b commit 88cfcb0

File tree

4 files changed

+796
-3
lines changed

4 files changed

+796
-3
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
import { v4 as uuidv4 } from 'uuid';
2+
3+
import { AuthTestUtils } from '../../support/auth-utils';
4+
import { getSlashMenuItemName } from '../../support/i18n-constants';
5+
import { testLog } from '../../support/test-helpers';
6+
import {
7+
AddPageSelectors,
8+
DropdownSelectors,
9+
ModalSelectors,
10+
PageSelectors,
11+
SlashCommandSelectors,
12+
SpaceSelectors,
13+
ViewActionSelectors,
14+
waitForReactUpdate,
15+
} from '../../support/selectors';
16+
17+
/**
18+
* Tests for Move Page Restrictions
19+
*
20+
* These tests verify that the "Move to" action is disabled for views that should not be movable:
21+
* - Case 1: Referenced database views (database inside database)
22+
* - Case 2: Children of database containers
23+
* - Case 3: Linked database views under documents
24+
*
25+
* Mirrors Desktop/Flutter implementation in view_ext.dart canBeDragged().
26+
*/
27+
describe('Move Page Restrictions', () => {
28+
const generateRandomEmail = () => `${uuidv4()}@appflowy.io`;
29+
const spaceName = 'General';
30+
31+
const ensureSpaceExpanded = (name: string) => {
32+
SpaceSelectors.itemByName(name).should('exist');
33+
SpaceSelectors.itemByName(name).then(($space) => {
34+
const expandedIndicator = $space.find('[data-testid="space-expanded"]');
35+
const isExpanded = expandedIndicator.attr('data-expanded') === 'true';
36+
37+
if (!isExpanded) {
38+
SpaceSelectors.itemByName(name).find('[data-testid="space-name"]').click({ force: true });
39+
waitForReactUpdate(500);
40+
}
41+
});
42+
};
43+
44+
const ensurePageExpandedByViewId = (viewId: string) => {
45+
cy.get(`[data-testid="page-${viewId}"]`)
46+
.first()
47+
.closest('[data-testid="page-item"]')
48+
.should('exist')
49+
.then(($page) => {
50+
const isExpanded = $page.find('[data-testid="outline-toggle-collapse"]').length > 0;
51+
52+
if (!isExpanded) {
53+
cy.wrap($page).find('[data-testid="outline-toggle-expand"]').first().click({ force: true });
54+
waitForReactUpdate(500);
55+
}
56+
});
57+
};
58+
59+
const currentDocumentViewIdFromDialog = () =>
60+
cy
61+
.get('[role="dialog"]:visible', { timeout: 20000 })
62+
.last()
63+
.find('[id^="editor-"]:not([id^="editor-title-"])', { timeout: 20000 })
64+
.first()
65+
.invoke('attr', 'id')
66+
.then((id) => (id ?? '').replace('editor-', ''));
67+
68+
beforeEach(() => {
69+
cy.on('uncaught:exception', (err) => {
70+
if (
71+
err.message.includes('Minified React error') ||
72+
err.message.includes('View not found') ||
73+
err.message.includes('No workspace or service found') ||
74+
err.message.includes('ResizeObserver loop')
75+
) {
76+
return false;
77+
}
78+
return true;
79+
});
80+
81+
cy.viewport(1280, 720);
82+
});
83+
84+
it('should disable Move to for linked database view under document (Case 3)', () => {
85+
const testEmail = generateRandomEmail();
86+
const sourceName = `SourceDB_${Date.now()}`;
87+
88+
testLog.testStart('Move to disabled for linked database view under document');
89+
testLog.info(`Test email: ${testEmail}`);
90+
91+
cy.visit('/login', { failOnStatusCode: false });
92+
cy.wait(2000);
93+
94+
const authUtils = new AuthTestUtils();
95+
authUtils.signInWithTestUrl(testEmail).then(() => {
96+
cy.url({ timeout: 30000 }).should('include', '/app');
97+
cy.wait(3000);
98+
99+
// 1) Create a standalone database (container exists in the sidebar)
100+
testLog.step(1, 'Create standalone Grid database');
101+
AddPageSelectors.inlineAddButton().first().click({ force: true });
102+
waitForReactUpdate(1000);
103+
AddPageSelectors.addGridButton().should('be.visible').click({ force: true });
104+
105+
// Rename container to a unique name
106+
ensureSpaceExpanded(spaceName);
107+
PageSelectors.itemByName('New Database').should('exist');
108+
PageSelectors.moreActionsButton('New Database').click({ force: true });
109+
ViewActionSelectors.renameButton().should('be.visible').click({ force: true });
110+
ModalSelectors.renameInput().should('be.visible').clear().type(sourceName);
111+
ModalSelectors.renameSaveButton().click({ force: true });
112+
waitForReactUpdate(2000);
113+
PageSelectors.itemByName(sourceName).should('exist');
114+
115+
// 2) Create a document page
116+
testLog.step(2, 'Create document page');
117+
AddPageSelectors.inlineAddButton().first().click({ force: true });
118+
waitForReactUpdate(1000);
119+
cy.get('[role="menuitem"]').first().click({ force: true });
120+
waitForReactUpdate(1000);
121+
122+
// Capture the document view id
123+
currentDocumentViewIdFromDialog().then((viewId) => {
124+
expect(viewId).to.not.equal('');
125+
cy.wrap(viewId).as('docViewId');
126+
cy.get(`#editor-${viewId}`, { timeout: 15000 }).should('exist');
127+
});
128+
waitForReactUpdate(1000);
129+
130+
// 3) Insert linked grid via slash menu
131+
testLog.step(3, 'Insert linked grid via slash menu');
132+
cy.get<string>('@docViewId').then((docViewId) => {
133+
cy.get(`#editor-${docViewId}`).should('exist').click('center', { force: true });
134+
cy.get(`#editor-${docViewId}`).type('/', { force: true });
135+
});
136+
waitForReactUpdate(500);
137+
138+
SlashCommandSelectors.slashPanel().should('be.visible').within(() => {
139+
SlashCommandSelectors.slashMenuItem(getSlashMenuItemName('linkedGrid')).first().click({ force: true });
140+
});
141+
waitForReactUpdate(1000);
142+
143+
SlashCommandSelectors.selectDatabase(sourceName);
144+
waitForReactUpdate(3000);
145+
146+
// 4) Expand the document to see linked view in sidebar
147+
testLog.step(4, 'Expand document and find linked view');
148+
ensureSpaceExpanded(spaceName);
149+
const referencedName = `View of ${sourceName}`;
150+
151+
cy.get<string>('@docViewId').then((docViewId) => {
152+
ensurePageExpandedByViewId(docViewId);
153+
waitForReactUpdate(1000);
154+
155+
// 5) Open More Actions for the linked database view
156+
testLog.step(5, 'Open More Actions for linked database view');
157+
158+
// Find the linked database view (child of the document)
159+
cy.get(`[data-testid="page-${docViewId}"]`)
160+
.first()
161+
.closest('[data-testid="page-item"]')
162+
.within(() => {
163+
// Find the linked view by its name
164+
cy.contains('[data-testid="page-name"]', referencedName)
165+
.closest('[data-testid="page-item"]')
166+
.trigger('mouseenter', { force: true })
167+
.trigger('mouseover', { force: true });
168+
});
169+
170+
waitForReactUpdate(500);
171+
172+
// Click the more actions button for the linked view
173+
cy.contains('[data-testid="page-name"]', referencedName)
174+
.closest('[data-testid="page-item"]')
175+
.find('[data-testid="page-more-actions"]')
176+
.first()
177+
.click({ force: true });
178+
179+
waitForReactUpdate(500);
180+
181+
// 6) Verify Move to is disabled
182+
testLog.step(6, 'Verify Move to is disabled');
183+
DropdownSelectors.content().should('be.visible');
184+
DropdownSelectors.content().within(() => {
185+
// Find the "Move to" menu item and verify it's disabled
186+
// Radix UI sets data-disabled="" (empty string) when disabled
187+
cy.contains('Move to')
188+
.closest('[role="menuitem"]')
189+
.should('have.attr', 'data-disabled');
190+
});
191+
});
192+
193+
testLog.testEnd('Move to disabled for linked database view under document');
194+
});
195+
});
196+
197+
it('should enable Move to for regular document pages', () => {
198+
const testEmail = generateRandomEmail();
199+
200+
testLog.testStart('Move to enabled for regular document pages');
201+
testLog.info(`Test email: ${testEmail}`);
202+
203+
cy.visit('/login', { failOnStatusCode: false });
204+
cy.wait(2000);
205+
206+
const authUtils = new AuthTestUtils();
207+
authUtils.signInWithTestUrl(testEmail).then(() => {
208+
cy.url({ timeout: 30000 }).should('include', '/app');
209+
cy.wait(3000);
210+
211+
// Wait for sidebar and find Getting started page
212+
ensureSpaceExpanded(spaceName);
213+
waitForReactUpdate(2000);
214+
215+
// Hover over Getting started page
216+
PageSelectors.itemByName('Getting started')
217+
.trigger('mouseenter', { force: true })
218+
.trigger('mouseover', { force: true });
219+
220+
waitForReactUpdate(500);
221+
222+
// Click more actions
223+
PageSelectors.moreActionsButton('Getting started').click({ force: true });
224+
225+
waitForReactUpdate(500);
226+
227+
// Verify Move to is NOT disabled for regular pages
228+
// Radix UI sets data-disabled attribute when disabled, so we check it doesn't exist
229+
DropdownSelectors.content().should('be.visible');
230+
DropdownSelectors.content().within(() => {
231+
cy.contains('Move to')
232+
.closest('[role="menuitem"]')
233+
.should('not.have.attr', 'data-disabled');
234+
});
235+
236+
testLog.testEnd('Move to enabled for regular document pages');
237+
});
238+
});
239+
240+
it('should enable Move to for database containers under document', () => {
241+
const testEmail = generateRandomEmail();
242+
const docName = `TestDoc_${Date.now()}`;
243+
244+
testLog.testStart('Move to enabled for database containers');
245+
testLog.info(`Test email: ${testEmail}`);
246+
247+
cy.visit('/login', { failOnStatusCode: false });
248+
cy.wait(2000);
249+
250+
const authUtils = new AuthTestUtils();
251+
authUtils.signInWithTestUrl(testEmail).then(() => {
252+
cy.url({ timeout: 30000 }).should('include', '/app');
253+
cy.wait(3000);
254+
255+
// 1) Create a document page first
256+
testLog.step(1, 'Create document page');
257+
AddPageSelectors.inlineAddButton().first().click({ force: true });
258+
waitForReactUpdate(1000);
259+
cy.get('[role="menuitem"]').first().click({ force: true });
260+
waitForReactUpdate(1000);
261+
262+
// Capture the document view id
263+
currentDocumentViewIdFromDialog().then((viewId) => {
264+
expect(viewId).to.not.equal('');
265+
cy.wrap(viewId).as('docViewId');
266+
cy.get(`#editor-${viewId}`, { timeout: 15000 }).should('exist');
267+
});
268+
waitForReactUpdate(1000);
269+
270+
// 2) Insert NEW grid via slash menu (creates container)
271+
testLog.step(2, 'Insert new grid via slash menu');
272+
cy.get<string>('@docViewId').then((docViewId) => {
273+
cy.get(`#editor-${docViewId}`).should('exist').click('center', { force: true });
274+
cy.get(`#editor-${docViewId}`).type('/', { force: true });
275+
});
276+
waitForReactUpdate(500);
277+
278+
SlashCommandSelectors.slashPanel().should('be.visible').within(() => {
279+
// This creates a NEW database (with container), not a linked view
280+
SlashCommandSelectors.slashMenuItem(getSlashMenuItemName('grid')).first().click({ force: true });
281+
});
282+
waitForReactUpdate(3000);
283+
284+
// 3) Expand the document to see the database container in sidebar
285+
testLog.step(3, 'Expand document and find database container');
286+
ensureSpaceExpanded(spaceName);
287+
288+
cy.get<string>('@docViewId').then((docViewId) => {
289+
ensurePageExpandedByViewId(docViewId);
290+
waitForReactUpdate(1000);
291+
292+
// 4) Find the database container (it should be named "Grid" or similar)
293+
testLog.step(4, 'Open More Actions for database container');
294+
295+
cy.get(`[data-testid="page-${docViewId}"]`)
296+
.first()
297+
.closest('[data-testid="page-item"]')
298+
.within(() => {
299+
// The database container should be a child - find it and hover
300+
cy.get('[data-testid="page-item"]')
301+
.first()
302+
.trigger('mouseenter', { force: true })
303+
.trigger('mouseover', { force: true });
304+
});
305+
306+
waitForReactUpdate(500);
307+
308+
// Click the more actions button for the database container
309+
cy.get(`[data-testid="page-${docViewId}"]`)
310+
.first()
311+
.closest('[data-testid="page-item"]')
312+
.find('[data-testid="page-item"]')
313+
.first()
314+
.find('[data-testid="page-more-actions"]')
315+
.first()
316+
.click({ force: true });
317+
318+
waitForReactUpdate(500);
319+
320+
// 5) Verify Move to is NOT disabled for database containers
321+
// Radix UI sets data-disabled attribute when disabled, so we check it doesn't exist
322+
testLog.step(5, 'Verify Move to is enabled for database container');
323+
DropdownSelectors.content().should('be.visible');
324+
DropdownSelectors.content().within(() => {
325+
cy.contains('Move to')
326+
.closest('[role="menuitem"]')
327+
.should('not.have.attr', 'data-disabled');
328+
});
329+
});
330+
331+
testLog.testEnd('Move to enabled for database containers');
332+
});
333+
});
334+
});

0 commit comments

Comments
 (0)