MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m13s
The share dialog for notes and shared folders now offers "Bearbeiten" and "Nur Lesen" and stores write/read accordingly. Editors already edit through the sync engine since phase 2. Adds e2e/share.e2e.mjs: shares a note as editor through the dialog and checks that the recipient can edit it; e2e/run.sh runs both browser tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
109 lines
4.5 KiB
JavaScript
109 lines
4.5 KiB
JavaScript
// Shares a note as editor through the share dialog and checks that the editor can then edit it.
|
|
// Needs a server in demo mode (accounts admin/admin and user/user); see e2e/README.md.
|
|
import { chromium } from 'playwright';
|
|
|
|
const BASE = process.env.MINDSIFT_URL || 'http://localhost:18080';
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
let failures = 0;
|
|
const check = (cond, msg) => {
|
|
console.log(`${cond ? 'OK ' : 'FAIL'} ${msg}`);
|
|
if (!cond) failures++;
|
|
};
|
|
|
|
async function api(token, method, path, body) {
|
|
const res = await fetch(BASE + path, {
|
|
method,
|
|
headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) },
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
});
|
|
const text = await res.text();
|
|
if (!res.ok) throw new Error(`${method} ${path}: ${res.status} ${text}`);
|
|
return text ? JSON.parse(text) : null;
|
|
}
|
|
|
|
async function waitFor(fn, ms) {
|
|
const end = Date.now() + ms;
|
|
while (Date.now() < end) {
|
|
try {
|
|
const v = await fn();
|
|
if (v) return v;
|
|
} catch {}
|
|
await sleep(250);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function openDoc(browser, token, title) {
|
|
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
await ctx.addInitScript((t) => localStorage.setItem('mindsift_jwt_token', t), token);
|
|
const page = await ctx.newPage();
|
|
await page.goto(BASE);
|
|
await page.getByRole('button', { name: /Workspace/ }).first().click();
|
|
await page.getByText(title, { exact: true }).first().click();
|
|
await page.waitForFunction(() => document.querySelectorAll('textarea').length > 1, null, { timeout: 10000 });
|
|
return { ctx, page };
|
|
}
|
|
|
|
const TITLE = `Freigabe-Test ${Date.now()}`;
|
|
|
|
const main = async () => {
|
|
const admin = (await api(null, 'POST', '/api/v1/auth/login', { login: 'admin', password: 'admin' })).token;
|
|
const user = (await api(null, 'POST', '/api/v1/auth/login', { login: 'user', password: 'user' })).token;
|
|
const doc = await api(admin, 'POST', '/api/v1/documents', {
|
|
title: TITLE,
|
|
blocks: [{ id: `fr_${Date.now()}`, type: 'bullet', content: 'Vom Eigentümer', level: 0 }],
|
|
});
|
|
|
|
const browser = await chromium.launch();
|
|
const A = await openDoc(browser, admin, TITLE);
|
|
|
|
// Share as editor through the dialog
|
|
await A.page.getByTitle('Notiz teilen und Berechtigungen').click();
|
|
await A.page.getByRole('button', { name: 'Benutzer', exact: true }).click();
|
|
const search = A.page.getByPlaceholder(/Benutzer suchen/);
|
|
await search.click();
|
|
await search.type('user');
|
|
await sleep(300);
|
|
await search.press('ArrowDown');
|
|
await search.press('Enter');
|
|
const roleSelect = A.page.locator('select').filter({ has: A.page.locator('option[value="editor"]') }).last();
|
|
check((await roleSelect.isEnabled()) && (await roleSelect.isVisible()), 'Rollenauswahl ist im Dialog verfügbar');
|
|
await roleSelect.selectOption('editor');
|
|
await A.page.getByRole('button', { name: /Benutzer hinzufügen/ }).click();
|
|
const shares = await waitFor(async () => {
|
|
const list = await api(admin, 'GET', `/api/v1/documents/${doc.id}/shares`);
|
|
return list.length > 0 ? list : null;
|
|
}, 5000);
|
|
check(shares && shares[0].permission === 'write', `Freigabe gespeichert als ${shares?.[0]?.permission}`);
|
|
const badge = await A.page
|
|
.getByText('Bearbeiter', { exact: true })
|
|
.first()
|
|
.waitFor({ state: 'visible', timeout: 5000 })
|
|
.then(() => true)
|
|
.catch(() => false);
|
|
check(badge, 'Dialog zeigt die Rolle „Bearbeiter“');
|
|
|
|
// The editor can edit
|
|
const snap = await api(user, 'GET', `/api/v1/docs/${doc.id}/snapshot`);
|
|
check(snap.role === 'editor', `Empfänger hat die Rolle ${snap.role}`);
|
|
const B = await openDoc(browser, user, TITLE);
|
|
check(!(await B.page.getByText('schreibgeschützt mit dir geteilt', { exact: false }).first().isVisible().catch(() => false)), 'Kein Nur-Lesen-Hinweis beim Bearbeiter');
|
|
await B.page.getByText('Vom Eigentümer', { exact: true }).first().click();
|
|
await B.page.keyboard.press('End');
|
|
await B.page.keyboard.type(' und vom Bearbeiter');
|
|
const saved = await waitFor(async () => {
|
|
const s = await api(admin, 'GET', `/api/v1/docs/${doc.id}/snapshot`);
|
|
return s.blocks.some((b) => b.content === 'Vom Eigentümer und vom Bearbeiter');
|
|
}, 6000);
|
|
check(!!saved, 'Bearbeiter kann die geteilte Notiz bearbeiten');
|
|
|
|
await browser.close();
|
|
console.log(failures ? `${failures} FEHLGESCHLAGEN` : 'ALLE PRÜFUNGEN BESTANDEN');
|
|
process.exit(failures ? 1 : 0);
|
|
};
|
|
|
|
main().catch((e) => {
|
|
console.error(e);
|
|
process.exit(2);
|
|
});
|