0.2.1 Release (#32)
* feat(security): implement CSRF protection * chore: clean up CSRF implementation - Remove unused generateCsrfToken export from security.ts - Remove redundant /csrf-token path check (GET already exempt) - Restore defineConfig wrapper in vitest.config.ts for type safety * add K8S note in README, fix broken e2e * feat/upload-bar (#30) * feat/upload-bar: add a upload bar when user upload file, indicate the upload process * feat/save-loading-status: add save status when click back button from editor * fix: address PR review issues in upload and save features - Replace deprecated substr() with substring() in UploadContext - Fix broken error handling that checked stale task status - Fix missing useEffect dependency in UploadStatus - Fix CSS class conflict in progress bar styling - Add error recovery for save state in Editor (reset on failure) - Use .finally() instead of .then() to ensure refresh on upload failure - Fix inconsistent indentation in UploadContext * fix e2e tests --------- Co-authored-by: Zimeng Xiong <zxzimeng@gmail.com> * chore: pre-release v0.2.1-dev * Update backend/src/security.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix filename/math random UUID generation --------- Co-authored-by: AdrianAcala <adrianacala017@gmail.com> Co-authored-by: adamant368 <60790941+Yiheng-Liu@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -2,7 +2,6 @@ import { test, expect } from "@playwright/test";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import {
|
||||
API_URL,
|
||||
createDrawing,
|
||||
deleteDrawing,
|
||||
listDrawings,
|
||||
@@ -27,7 +26,7 @@ test.describe("Drag and Drop - Collections", () => {
|
||||
for (const id of createdDrawingIds) {
|
||||
try {
|
||||
await deleteDrawing(request, id);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
@@ -36,7 +35,7 @@ test.describe("Drag and Drop - Collections", () => {
|
||||
for (const id of createdCollectionIds) {
|
||||
try {
|
||||
await deleteCollection(request, id);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
@@ -61,7 +60,7 @@ test.describe("Drag and Drop - Collections", () => {
|
||||
|
||||
// Hover to reveal the collection picker
|
||||
await card.hover();
|
||||
|
||||
|
||||
// Click the collection picker button on the card
|
||||
const collectionPicker = card.locator(`[data-testid="collection-picker-${drawing.id}"]`);
|
||||
await collectionPicker.click();
|
||||
@@ -76,7 +75,7 @@ test.describe("Drag and Drop - Collections", () => {
|
||||
// Navigate to the collection and verify drawing is there
|
||||
await page.getByRole("navigation").getByRole("button", { name: collection.name }).click();
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
|
||||
await expect(card).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -85,9 +84,9 @@ test.describe("Drag and Drop - Collections", () => {
|
||||
const collection = await createCollection(request, `UnorgTest_Collection_${Date.now()}`);
|
||||
createdCollectionIds.push(collection.id);
|
||||
|
||||
const drawing = await createDrawing(request, {
|
||||
const drawing = await createDrawing(request, {
|
||||
name: `UnorgTest_Drawing_${Date.now()}`,
|
||||
collectionId: collection.id
|
||||
collectionId: collection.id
|
||||
});
|
||||
createdDrawingIds.push(drawing.id);
|
||||
|
||||
@@ -119,7 +118,7 @@ test.describe("Drag and Drop - Collections", () => {
|
||||
// Navigate to Unorganized and verify drawing is there
|
||||
await page.getByRole("navigation").getByRole("button", { name: "Unorganized" }).click();
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
|
||||
await expect(page.locator(`#drawing-card-${drawing.id}`)).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -146,7 +145,7 @@ test.describe("Drag and Drop - Collections", () => {
|
||||
// Select both drawings
|
||||
const card1 = page.locator(`#drawing-card-${drawing1.id}`);
|
||||
const card2 = page.locator(`#drawing-card-${drawing2.id}`);
|
||||
|
||||
|
||||
await card1.hover();
|
||||
const toggle1 = card1.locator(`[data-testid="select-drawing-${drawing1.id}"]`);
|
||||
await toggle1.click();
|
||||
@@ -186,7 +185,7 @@ test.describe("Drag and Drop - File Import", () => {
|
||||
for (const drawing of drawings) {
|
||||
try {
|
||||
await deleteDrawing(request, drawing.id);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
@@ -194,7 +193,7 @@ test.describe("Drag and Drop - File Import", () => {
|
||||
for (const id of createdDrawingIds) {
|
||||
try {
|
||||
await deleteDrawing(request, id);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
@@ -205,7 +204,7 @@ test.describe("Drag and Drop - File Import", () => {
|
||||
// Note: Simulating drag events with files is unreliable in Playwright
|
||||
// because the DataTransfer API has security restrictions.
|
||||
// This test verifies the drop zone UI exists and can be triggered.
|
||||
|
||||
|
||||
await page.goto("/");
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
@@ -218,13 +217,13 @@ test.describe("Drag and Drop - File Import", () => {
|
||||
try {
|
||||
const dt = new DataTransfer();
|
||||
dt.items.add(new File(['test'], 'test.excalidraw', { type: 'application/json' }));
|
||||
|
||||
|
||||
const event = new DragEvent('dragenter', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
dataTransfer: dt,
|
||||
});
|
||||
|
||||
|
||||
// Find the main content area and dispatch the event
|
||||
const main = document.querySelector('main');
|
||||
if (main) {
|
||||
@@ -242,7 +241,7 @@ test.describe("Drag and Drop - File Import", () => {
|
||||
// Check that the drop zone overlay is shown
|
||||
const dropZone = page.getByText("Drop files to import");
|
||||
const isVisible = await dropZone.isVisible().catch(() => false);
|
||||
|
||||
|
||||
if (isVisible) {
|
||||
await expect(dropZone).toBeVisible();
|
||||
} else {
|
||||
@@ -255,7 +254,7 @@ test.describe("Drag and Drop - File Import", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("should import excalidraw file via file input", async ({ page, request }, testInfo) => {
|
||||
test("should import excalidraw file via file input", async ({ page }, testInfo) => {
|
||||
await page.goto("/");
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
@@ -273,11 +272,8 @@ test.describe("Drag and Drop - File Import", () => {
|
||||
const fileInput = page.locator("#dashboard-import");
|
||||
await fileInput.setInputFiles(fixturePath);
|
||||
|
||||
// Wait for import success modal
|
||||
await expect(page.getByText("Import Successful")).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Dismiss the modal
|
||||
await page.getByRole("button", { name: "OK" }).click();
|
||||
// Wait for upload to complete - the UploadStatus component shows "Done" when finished
|
||||
await expect(page.getByText("Uploads (Done)")).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Search for the imported drawing (it uses the filename as name)
|
||||
await page.getByPlaceholder("Search drawings...").fill("small-image");
|
||||
|
||||
Reference in New Issue
Block a user