wrolpi/app/cypress/support/file-browser.js
lrnselfreliance 9278103554
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Controller Tests / controller-tests (push) Waiting to run
Jest Tests / jest-tests (push) Waiting to run
e2e: exercise the FileBrowser footer buttons against real files (#670)
* 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>
2026-08-19 20:27:29 -06:00

79 lines
3.4 KiB
JavaScript

/*
* Shared helpers for the FileBrowser e2e specs (cypress/e2e/files/).
*
* These specs run against the live stack with real files: the fb:* tasks
* (cypress.config.js) seed a `cypress-fb/` sandbox inside the media directory,
* and cy.refreshFiles (support/commands.js) keeps the DB in sync with it.
*/
export const SANDBOX = 'cypress-fb';
export const footerButton = (label) => cy.get('.sticky-footer').contains('button', label);
export const row = (name) => cy.contains('tr', name);
export const selectRow = (name) => row(name).find('input[type="checkbox"]').check({force: true});
export const deselectRow = (name) => row(name).find('input[type="checkbox"]').uncheck({force: true});
export const dialog = () => cy.get('[role="dialog"]');
// WROL mode silently disables most footer buttons; fail loudly instead of reporting
// false failures. Call once from a spec's before().
export const assertWROLModeOff = () => {
cy.request('/api/settings').its('body.wrol_mode').then((wrolMode) => {
expect(wrolMode, 'WROL mode must be OFF for FileBrowser e2e tests (Admin > Settings)')
.to.be.false;
});
};
/*
* Seed the sandbox, sync the DB, open /files, and open the sandbox folder.
* `firstRow` should be a seeded name that proves the folder's children rendered.
*/
export const setupFileBrowser = (seedFiles, firstRow) => {
cy.task('fb:reset', {files: seedFiles});
cy.refreshFiles([SANDBOX]);
// The footer shows labeled buttons only when it is at least 1050px wide.
cy.viewport(1280, 800);
cy.visit('/files');
// A fresh page load replays recent events as toasts (each test run adds more), and
// they stack over the sticky footer, making cy.click() refuse the buttons. These
// specs assert on the disk and the table, not toasts, so hide the notification layer.
cy.document().then((doc) => {
const style = doc.createElement('style');
style.textContent = '.mantine-Notifications-root { display: none !important; }';
doc.head.appendChild(style);
});
cy.contains('.file-path', SANDBOX).click();
row(firstRow).should('be.visible');
};
// Empty the sandbox on disk, let a refresh purge its rows from the DB, then remove the
// empty directory itself so nothing stale survives into the next run. Call from after().
//
// The refresh is best-effort and never fails: a failed command aborts the rest of its hook,
// and the disk wipe below must always run. The next run's setup refresh repairs the DB
// even when this purge is missed.
export const teardownFileBrowser = () => {
cy.task('fb:reset', {files: {}});
cy.serverNow().then((since) => {
cy.request({
method: 'POST',
url: '/api/files/refresh',
body: {paths: [SANDBOX]},
failOnStatusCode: false,
});
const deadline = Date.now() + 30000;
const poll = () => {
cy.request({
url: `/api/events/feed?after=${encodeURIComponent(since)}`,
failOnStatusCode: false,
}).then(({body}) => {
const done = ((body && body.events) || []).some((e) =>
e.event === 'directory_refresh' && e.message === `Refreshed: ${SANDBOX}`);
if (done || Date.now() > deadline) return;
cy.wait(1000);
poll();
});
};
poll();
});
cy.task('fb:cleanup');
};