microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
67.11k stars 3.69k forks source link

[Bug]: toBeEditable() returns true when locator is not editable #33697

Open qk5 opened 1 day ago

qk5 commented 1 day ago

Version

Tried all versions from: 1.45.0 to 1.49.0

Steps to reproduce

Here is my simple html page:

<html><body><span id="text1">Text 1</span></body></html>

And here is my playwright script:

let textLocator = await page.locator('#text1');
await expect(textLocator).toBeEditable();

Expected behavior

Expected locator not to be editable

Actual behavior

Playwright says locator is editable

Additional context

No response

Environment

OS: Windows 10 10.0.19045
yabi90 commented 1 day ago

The issue you're facing happens because the toBeEditable() assertion in Playwright checks if the element is interactable and can be edited by the user, but the <span> tag is inherently not an editable element in HTML.

In HTML, only certain elements (like <input>, <textarea>, or elements with the contenteditable attribute) are considered editable. A <span> tag by default is not editable, so it won't trigger any "editable" behavior unless you explicitly make it so (e.g., by adding the contenteditable attribute to it).

Why is this happening?

Playwright's toBeEditable() assertion works under the assumption that the element is a form field or an editable container (like an input field or a contenteditable element). Since your <span> element is not editable by default, Playwright is interpreting it as editable if it can interact with the element, and the default behavior of the toBeEditable() assertion is satisfied.

Solution

To fix this, you have two options depending on your requirements:

  1. Change the span to an editable element (if that's your goal): If you want the element to be truly editable (e.g., to test interactions), you can add the contenteditable attribute to your <span> tag like so:

    <html>
     <body>
       <span id="text1" contenteditable="true">Text 1</span>
     </body>
    </html>

    This will make the <span> editable and toBeEditable() will pass as expected.

  2. Change your assertion to check for the correct behavior (if <span> should not be editable): If you do not intend for the <span> to be editable, you should use an assertion that is better suited to check whether the element is editable. You can assert that the element is not editable like this:

    let textLocator = await page.locator('#text1');
    await expect(textLocator).not.toBeEditable();

    This will correctly pass, as the <span> is not editable.

Summary

I hope this helps resolve the issue you encountered.

qk5 commented 1 day ago

@yabi90

Solution 1 - It won't work in my case because it is a partner site, and our development team has no control over them.

For Solution 2 - > let textLocator = await page.locator('#text1'); > await expect(textLocator).not.toBeEditable();

I tried this but this is not working, playwrights says expected: not to be editable, but the locator is editable. Just double checking, for this to work, I still need contenteditable="true" within the tag in the html page, right?

yabi90 commented 1 day ago

To clarify, you do not need contenteditable="true" for the not.toBeEditable() assertion to work. The issue is likely due to Playwright interpreting the <span> as editable because it’s interactable in some way, even though it’s not an editable element by default.

Here are some steps to resolve this:

  1. Check Interactivity: Ensure the <span> isn’t being made interactable by JavaScript or CSS (e.g., handling events or styled to appear editable).

  2. Use Alternative Assertions:

    • Try toBeDisabled() if the element should be non-interactive.
    • Use isEditable() to check the element’s actual editable state directly.
  3. Use isEnabled(): Assert that the element is not enabled for interaction:

    let textLocator = await page.locator('#text1');
    const isEnabled = await textLocator.isEnabled();
    expect(isEnabled).toBe(false);

By using these approaches, you can ensure that Playwright recognizes the <span> as non-editable, without needing to modify the HTML.

qk5 commented 1 day ago

Thank you for the suggestions.

My html page is very simple, there is no CSS or JavaScript involved, as follow: <html><body><span id="text1">Text 1</span></body></html>

I tried your second and third approaches, by using toBeDisabled(), isEditable(), and isEnabled(). However, none of them worked for me.

toBeDisalbed() - playwright message: Expected: disabled, Received: enabled isEditable() - playwright message: Expected: false, Received: true isEnabled() - returns true