mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
* e2e: exercise the FileBrowser footer buttons against real files Sixteen Cypress tests covering Rename, Move, Delete (including the tagged-file force-delete flow), Ignore/Unignore, Upload, and scoped Refresh, with no API mocking: fb:* tasks seed a cypress-fb/ sandbox inside the media directory, the UI mutates it through the real API, and assertions check the disk as well as the table. Skipped in CI, which runs only the React dev server. Background jobs (move, refresh) only queue work and worker_status reads idle while jobs wait, so cy.refreshFiles / cy.waitForEvent poll the events feed for completion events newer than the server's clock, and fail loudly when one never arrives. Every suite cleans up unconditionally: sandbox files, DB rows via a final refresh, test tags and their tags/ directories, and any leaked ignored-directory config. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * e2e: act on review — drain stragglers, teardown that cannot be skipped Events carry no job id, so waitForEvent cannot prove WHICH job finished. The rename tests now drain the parent-directory refresh their rename queues before ending, so the next test's refresh wait cannot be satisfied by a straggler while the DB still reflects the old tree; move waits match the destination in the completion message. Teardown must always reach the disk wipe: a failed command aborts the rest of its hook, so the tag cleanup and the DB-purge refresh are now best-effort (failOnStatusCode: false, no assertions), and the unignore runs after every test instead of once per spec. History and forward-looking product notes trimmed from comments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
136 lines
5.2 KiB
JavaScript
136 lines
5.2 KiB
JavaScript
/*
|
|
* The Delete button in the FileBrowser's sticky footer, exercised against the live stack.
|
|
*
|
|
* Real files: the fb:* tasks seed a `cypress-fb/` sandbox in the media directory and the UI
|
|
* deletes them through the real API (POST /api/files/delete runs synchronously). Deleting a
|
|
* TAGGED file is refused with FILE_GROUP_IS_TAGGED, which opens a second confirmation listing
|
|
* the tagged files; confirming there force-deletes.
|
|
*/
|
|
import {
|
|
assertWROLModeOff,
|
|
dialog,
|
|
footerButton,
|
|
row,
|
|
selectRow,
|
|
setupFileBrowser,
|
|
teardownFileBrowser,
|
|
} from '../../support/file-browser';
|
|
|
|
// Requires the full stack (API + real media directory); CI runs only the React dev server.
|
|
(Cypress.env('CI') ? describe.skip : describe)('FileBrowser footer: Delete', () => {
|
|
|
|
const seedFiles = {
|
|
'fb-notes.txt': 'delete me',
|
|
'fb-other.txt': 'leave me alone',
|
|
'fb-alpha/fb-one.txt': 'child one',
|
|
'fb-alpha/fb-two.txt': 'child two',
|
|
'fb-tagged.txt': 'tagged, delete needs force',
|
|
};
|
|
|
|
const TAG_NAME = 'CypressFBDelete';
|
|
|
|
// Remove the suite's tag wherever a failed run may have left it, along with the
|
|
// tags/<TAG_NAME>/ directory: force-deleting a tagged file strands its hardlink there.
|
|
// failOnStatusCode: false throughout -- a failed command aborts the rest of its hook,
|
|
// and this runs in teardown, where an API hiccup must not skip the disk cleanup.
|
|
const deleteTestTag = () => {
|
|
cy.request({url: '/api/tag', failOnStatusCode: false}).then(({body}) => {
|
|
const tag = ((body && body.tags) || []).find((t) => t.name === TAG_NAME);
|
|
if (tag) {
|
|
cy.request({
|
|
method: 'DELETE',
|
|
url: `/api/tag/${tag.id}`,
|
|
failOnStatusCode: false,
|
|
});
|
|
}
|
|
});
|
|
cy.task('fb:cleanup-tag-dir', TAG_NAME);
|
|
};
|
|
|
|
before(() => {
|
|
assertWROLModeOff();
|
|
deleteTestTag();
|
|
});
|
|
|
|
beforeEach(() => setupFileBrowser(seedFiles, 'fb-notes.txt'));
|
|
|
|
after(() => {
|
|
deleteTestTag();
|
|
teardownFileBrowser();
|
|
});
|
|
|
|
// The footer Delete goes through APIButton's confirmation first.
|
|
const confirmDialog = () => {
|
|
dialog().should('be.visible')
|
|
.should('contain.text', 'Are you sure you want to delete these files?');
|
|
return dialog();
|
|
};
|
|
|
|
it('cancelling the confirmation deletes nothing', () => {
|
|
selectRow('fb-notes.txt');
|
|
footerButton('Delete').click();
|
|
|
|
confirmDialog().contains('button', 'Cancel').click();
|
|
dialog().should('not.exist');
|
|
|
|
row('fb-notes.txt').should('be.visible');
|
|
cy.task('fb:exists', 'fb-notes.txt').should('be.true');
|
|
});
|
|
|
|
it('deletes one file from disk', () => {
|
|
selectRow('fb-notes.txt');
|
|
footerButton('Delete').click();
|
|
confirmDialog().contains('button', 'Delete').click();
|
|
|
|
cy.contains('tr', 'fb-notes.txt').should('not.exist');
|
|
cy.task('fb:exists', 'fb-notes.txt').should('be.false');
|
|
cy.task('fb:exists', 'fb-other.txt').should('be.true');
|
|
});
|
|
|
|
it('deletes an open directory recursively without breaking the table', () => {
|
|
// Open the folder first so it is in openFolders; deleting it must also prune it
|
|
// from openFolders or the next fetch requests a directory that no longer exists.
|
|
cy.contains('.file-path', 'fb-alpha').click();
|
|
row('fb-one.txt').should('be.visible');
|
|
|
|
selectRow('fb-alpha');
|
|
footerButton('Delete').click();
|
|
confirmDialog().contains('button', 'Delete').click();
|
|
|
|
cy.contains('tr', /fb-alpha$/).should('not.exist');
|
|
cy.task('fb:exists', 'fb-alpha').should('be.false');
|
|
|
|
// The table still renders the remaining files -- no failed re-fetch.
|
|
row('fb-other.txt').should('be.visible');
|
|
});
|
|
|
|
it('a tagged file needs the second, force-delete confirmation', () => {
|
|
// Tag the file through the real API; the FileGroup exists because setup refreshed.
|
|
cy.request('POST', '/api/tag', {name: TAG_NAME, color: '#ff0000'});
|
|
cy.request('POST', '/api/files/tag', {
|
|
tag_name: TAG_NAME,
|
|
file_group_primary_path: 'cypress-fb/fb-tagged.txt',
|
|
});
|
|
|
|
// First attempt: cancel at the tagged-files warning; nothing is deleted.
|
|
selectRow('fb-tagged.txt');
|
|
footerButton('Delete').click();
|
|
confirmDialog().contains('button', 'Delete').click();
|
|
|
|
dialog().should('contain.text', 'Tagged Files Will Be Deleted')
|
|
.should('contain.text', 'cypress-fb/fb-tagged.txt')
|
|
.should('contain.text', TAG_NAME);
|
|
dialog().contains('button', 'Cancel').click();
|
|
dialog().should('not.exist');
|
|
cy.task('fb:exists', 'fb-tagged.txt').should('be.true');
|
|
|
|
// Second attempt: confirm the warning; the file is force-deleted.
|
|
footerButton('Delete').click();
|
|
confirmDialog().contains('button', 'Delete').click();
|
|
dialog().should('contain.text', 'Tagged Files Will Be Deleted');
|
|
dialog().contains('button', 'Delete').click();
|
|
|
|
cy.contains('tr', 'fb-tagged.txt').should('not.exist');
|
|
cy.task('fb:exists', 'fb-tagged.txt').should('be.false');
|
|
});
|
|
});
|