ci / check (push) Successful in 57s
Sobe web/index.html + web/js/app.js de verdade em jsdom, intercepta a API e cobre arrastar-e-soltar entre pastas, mover/renomear/excluir, filtros, busca e preview. CI passa a rodar npm ci && npm test.
162 lines
5.6 KiB
JavaScript
162 lines
5.6 KiB
JavaScript
// Arrastar-e-soltar de arquivos entre pastas no gerenciador.
|
|
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert');
|
|
const { createApp, FakeDataTransfer } = require('./harness');
|
|
|
|
test('arrastar um arquivo para uma pasta move o arquivo', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
const source = app.rowByName('raiz.txt');
|
|
const target = app.treeRowByName('Documentos');
|
|
assert.ok(source, 'arquivo na raiz deve aparecer na lista');
|
|
assert.ok(target, 'pasta deve aparecer na árvore');
|
|
|
|
app.dragAndDrop(source, target);
|
|
|
|
const req = app.lastRequest('PUT', '/api/files/file2/move');
|
|
assert.ok(req, 'deve chamar PUT /api/files/{id}/move');
|
|
assert.deepStrictEqual(req.body, { folder: 'folder1' });
|
|
});
|
|
|
|
test('o arrasto leva o id do arquivo no payload interno', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
const dt = app.dragAndDrop(app.rowByName('raiz.txt'), app.treeRowByName('Documentos'));
|
|
|
|
assert.strictEqual(dt.getData('application/x-notas-file'), 'file2');
|
|
assert.strictEqual(dt.getData('text/plain'), 'raiz.txt');
|
|
});
|
|
|
|
test('soltar na pasta em que o arquivo já está não chama a API', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
app.treeRowByName('Documentos').click();
|
|
await app.settle();
|
|
const source = app.rowByName('nota.txt');
|
|
assert.ok(source, 'nota.txt deve estar na lista da pasta');
|
|
|
|
app.dragAndDrop(source, app.treeRowByName('Documentos'));
|
|
|
|
assert.strictEqual(app.requests('PUT', '/move').length, 0, 'nenhuma chamada de movementação');
|
|
});
|
|
|
|
test('soltar na raiz move o arquivo para a raiz', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
app.treeRowByName('Documentos').click();
|
|
await app.settle();
|
|
app.dragAndDrop(app.rowByName('nota.txt'), app.treeRowByName('Raiz'));
|
|
|
|
const req = app.lastRequest('PUT', '/api/files/file1/move');
|
|
assert.ok(req, 'deve chamar PUT /api/files/{id}/move');
|
|
assert.deepStrictEqual(req.body, { folder: '' });
|
|
});
|
|
|
|
test('drop de arquivo do sistema é ignorado', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
const dt = new FakeDataTransfer(); // sem mime interno: é um arquivo do disco
|
|
app.fireDrag(app.rowByName('raiz.txt'), 'dragover', dt);
|
|
app.fireDrag(app.treeRowByName('Documentos'), 'drop', dt);
|
|
|
|
assert.strictEqual(app.requests('PUT', '/move').length, 0, 'não deve mover nada');
|
|
});
|
|
|
|
test('a pasta alvo é destacada durante o arrasto', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
const source = app.rowByName('raiz.txt');
|
|
const target = app.treeRowByName('Documentos');
|
|
const dt = new FakeDataTransfer();
|
|
|
|
app.fireDrag(source, 'dragstart', dt);
|
|
assert.ok(source.classList.contains('dragging'), 'origem fica marcada como arrastando');
|
|
|
|
app.fireDrag(target, 'dragover', dt);
|
|
assert.ok(target.classList.contains('drop-hl'), 'destino fica destacado');
|
|
|
|
app.fireDrag(source, 'dragend', dt);
|
|
assert.ok(!target.classList.contains('drop-hl'), 'destaque é limpo ao fim do arrasto');
|
|
assert.ok(!source.classList.contains('dragging'), 'origem volta ao normal');
|
|
});
|
|
|
|
test('o destino da movimentação é informado ao usuário', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
app.dragAndDrop(app.rowByName('raiz.txt'), app.treeRowByName('Documentos'));
|
|
await app.settle();
|
|
|
|
assert.match(app.toastText(), /Documentos/);
|
|
});
|
|
|
|
test('após mover, o gerenciador recarrega a árvore', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
const before = app.requests('GET', '/api/files/tree').length;
|
|
app.dragAndDrop(app.rowByName('raiz.txt'), app.treeRowByName('Documentos'));
|
|
await app.settle();
|
|
|
|
assert.ok(app.requests('GET', '/api/files/tree').length > before, 'a árvore deve ser recarregada');
|
|
});
|
|
|
|
test('a falha da API não deixa a UI afirmar sucesso', async (t) => {
|
|
const app = createApp({
|
|
routes: {
|
|
'/api/files/file2/move': ({ json }) => json({ error: 'pasta inexistente' }, 400),
|
|
},
|
|
});
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
app.dragAndDrop(app.rowByName('raiz.txt'), app.treeRowByName('Documentos'));
|
|
await app.settle();
|
|
|
|
assert.match(app.toastText(), /pasta inexistente/, 'o erro da API aparece para o usuário');
|
|
assert.doesNotMatch(app.toastText(), /Movido para/, 'não pode anunciar sucesso');
|
|
});
|
|
|
|
test('no modo seletor as linhas não são arrastáveis', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
|
|
// abre o gerenciador como seletor (botão 📎 Anexo)
|
|
app.document.querySelector('#btn-attach').click();
|
|
await app.waitFor(() => app.listRows().length > 0);
|
|
|
|
for (const row of app.listRows()) {
|
|
assert.strictEqual(row.draggable, false, 'linha do seletor não deve ser arrastável');
|
|
}
|
|
});
|
|
|
|
test('o arquivo continua vinculado à nota após mudar de pasta', async (t) => {
|
|
const app = createApp();
|
|
t.after(() => app.close());
|
|
await app.openManager();
|
|
|
|
app.dragAndDrop(app.rowByName('raiz.txt'), app.treeRowByName('Documentos'));
|
|
await app.settle();
|
|
|
|
// a movimentação não mexe no vínculo com a nota: só o id e a pasta mudam
|
|
const req = app.lastRequest('PUT', '/api/files/file2/move');
|
|
assert.deepStrictEqual(Object.keys(req.body), ['folder']);
|
|
assert.strictEqual(req.url, '/api/files/file2/move', 'o id do arquivo é o mesmo, então a referência da nota continua válida');
|
|
});
|