Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
qk5 opened this issue Nov 20, 2024 · 4 comments · Fixed by #33713
Closed

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

qk5 opened this issue Nov 20, 2024 · 4 comments · Fixed by #33713
Assignees
Labels

Comments

@qk5
Copy link

qk5 commented Nov 20, 2024

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
Copy link

yabi90 commented Nov 20, 2024

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

  • The toBeEditable() assertion applies to elements like <input>, <textarea>, or those with the contenteditable attribute.
  • If you want the element to be considered editable, add the contenteditable="true" attribute.
  • If you don't want the element to be editable, use not.toBeEditable() to ensure the assertion passes when the element is not editable.

I hope this helps resolve the issue you encountered.

@qk5
Copy link
Author

qk5 commented Nov 20, 2024

@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
Copy link

yabi90 commented Nov 20, 2024

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
Copy link
Author

qk5 commented Nov 21, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants