# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: sources.spec.ts >> Board access >> preview text content is readable but cannot be edited
- Location: e2e/sources.spec.ts:290:7

# Error details

```
Error: expect(locator).toHaveAttribute(expected) failed

Locator:  locator('.react-flow__node[data-id="source-11"]').locator('.tiptap')
Expected: "false"
Received: "true"
Timeout:  10000ms

Call log:
  - Expect "toHaveAttribute" locator('.react-flow__node[data-id="source-11"]').locator('.tiptap') with timeout 10000ms
  - waiting for locator('.react-flow__node[data-id="source-11"]').locator('.tiptap')
    24 × locator resolved to <div tabindex="0" translate="no" contenteditable="true" class="tiptap ProseMirror focus:outline-hidden p-1 min-h-[200px]">…</div>
       - unexpected value "true"

```

```yaml
- paragraph: A read-only preview observation
```

# Test source

```ts
  214 |     await page.goto("/board/3");
  215 |     await expect(page.locator(".react-flow")).toBeVisible();
  216 |     for (const name of ["Add Text", "Sticky Note", "AI Assistant", "Add Website", "Create group"]) {
  217 |       await expect(
  218 |         page.locator(".flow-toolbar").getByRole("button", { name, exact: true })
  219 |       ).toBeDisabled();
  220 |     }
  221 |     await expect(page.getByRole("button", { name: "Share board", exact: true })).toHaveCount(0);
  222 |     await page.locator(".react-flow__pane").click({ position: { x: 1100, y: 500 } });
  223 |     await page.keyboard.press("t");
  224 |     await page.keyboard.press("Control+a");
  225 |     await page.keyboard.press("Delete");
  226 |     expect(
  227 |       (await backend.requests()).filter(
  228 |         (r) =>
  229 |           (r.method === "POST" &&
  230 |             ["/source/text", "/chat-node/3", "/board/3/items/batch/delete"].includes(r.path)) ||
  231 |           r.method === "DELETE"
  232 |       )
  233 |     ).toHaveLength(0);
  234 |   });
  235 | 
  236 |   test("viewer cannot import files by dropping them onto a read-only board", async ({
  237 |     page,
  238 |     backend,
  239 |   }) => {
  240 |     await page.goto("/board/3");
  241 |     await expect(
  242 |       page.locator(".flow-toolbar").getByRole("button", { name: "Add Text", exact: true })
  243 |     ).toBeDisabled();
  244 |     const transfer = await page.evaluateHandle(() => {
  245 |       const data = new DataTransfer();
  246 |       data.items.add(
  247 |         new File(["This must remain read-only"], "viewer.txt", { type: "text/plain" })
  248 |       );
  249 |       return data;
  250 |     });
  251 |     await page
  252 |       .locator(".react-flow__pane")
  253 |       .dispatchEvent("drop", { dataTransfer: transfer, clientX: 650, clientY: 450 });
  254 |     await settleFileDrop(page, transfer);
  255 |     await expect(page.locator(".react-flow__node-text")).toHaveCount(0);
  256 |     await page.reload();
  257 |     await expect(
  258 |       page.locator(".flow-toolbar").getByRole("button", { name: "Add Text", exact: true })
  259 |     ).toBeDisabled();
  260 |     expect(
  261 |       (await backend.requests()).filter((r) => r.method === "POST" && r.path === "/source/text")
  262 |     ).toHaveLength(0);
  263 |   });
  264 | 
  265 |   test("preview boards cannot import files through drag and drop", async ({ page, backend }) => {
  266 |     await page.goto("/board/1?preview=1");
  267 |     await expect(page.locator(".react-flow")).toBeVisible();
  268 |     await expect(
  269 |       page.locator(".flow-toolbar").getByRole("button", { name: "Add Text", exact: true })
  270 |     ).toHaveCount(0);
  271 |     const transfer = await page.evaluateHandle(() => {
  272 |       const data = new DataTransfer();
  273 |       data.items.add(
  274 |         new File(["This preview must remain unchanged"], "preview.txt", { type: "text/plain" })
  275 |       );
  276 |       return data;
  277 |     });
  278 |     await page
  279 |       .locator(".react-flow__pane")
  280 |       .dispatchEvent("drop", { dataTransfer: transfer, clientX: 650, clientY: 450 });
  281 |     await settleFileDrop(page, transfer);
  282 |     await expect(page.locator(".react-flow__node-text")).toHaveCount(0);
  283 |     await page.reload();
  284 |     await expect(page.locator(".react-flow")).toBeVisible();
  285 |     expect(
  286 |       (await backend.requests()).filter((r) => r.method === "POST" && r.path === "/source/text")
  287 |     ).toHaveLength(0);
  288 |   });
  289 | 
  290 |   test("preview text content is readable but cannot be edited", async ({ page, backend }) => {
  291 |     await backend.configure({
  292 |       sources: [
  293 |         {
  294 |           id: 11,
  295 |           boardId: 1,
  296 |           sourceId: "11",
  297 |           positionX: 0,
  298 |           positionY: 0,
  299 |           styles: JSON.stringify({ width: 600, height: 400, mode: "full" }),
  300 |           updateId: 0,
  301 |           source: {
  302 |             id: 11,
  303 |             type: "text",
  304 |             status: "success",
  305 |             title: "Preview research",
  306 |             text: "<p>A read-only preview observation</p>",
  307 |           },
  308 |         },
  309 |       ],
  310 |     });
  311 |     await page.goto("/board/1?preview=1");
  312 |     const node = page.locator('.react-flow__node[data-id="source-11"]');
  313 |     await expect(node).toContainText("A read-only preview observation");
> 314 |     await expect(node.locator(".tiptap")).toHaveAttribute("contenteditable", "false");
      |                                           ^ Error: expect(locator).toHaveAttribute(expected) failed
  315 |     expect(
  316 |       (await backend.requests()).filter((r) => r.method === "PATCH" && r.path === "/source/11/text")
  317 |     ).toHaveLength(0);
  318 |   });
  319 | 
  320 |   test("missing board shows an actionable access error instead of an endless spinner", async ({
  321 |     page,
  322 |   }) => {
  323 |     await page.goto("/board/99999");
  324 |     await expect(page.getByText("Failed to load board", { exact: false })).toBeVisible();
  325 |     await expect(page.getByRole("button", { name: "Go to home", exact: false })).toBeVisible();
  326 |     await expect(page.locator(".react-flow")).toHaveCount(0);
  327 |   });
  328 | });
  329 | 
```