From cdbb9a469aa56e2282f5d5525dde4dd796f3b3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Kondratiuk?= Date: Sat, 28 Mar 2026 15:20:07 -0300 Subject: [PATCH] Fix elementhandle-convenience.spec.ts headful test failures Align ElementHandleConvenienceTests with upstream Playwright to fix headful-mode failures: - Move 4 "should be atomic" tests to SelectorsRegisterTests where they belong in upstream (selectors-register.spec.ts), removing the stale create() method from selector engine definitions - Fix InnerTextShouldThrow: assert exception2.Message instead of exception1.Message (copy-paste bug) - Fix TextContentShouldWork: use #inner selector matching upstream - Fix PlaywrightTest attribute: remove erroneous leading quote in "'innerText should throw" - Add missing null-return assertions in GetAttributeShouldWork - Add missing no-such-element assertions in IsVisibleAndIsHiddenShouldWork - Remove elementhandle-convenience.spec.ts headful expected-failure entry Closes #111 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../TestExpectations.local.json | 6 - .../ElementHandleConvenienceTests.cs | 120 ++---------------- .../SelectorsRegisterTests.cs | 104 +++++++++++++++ 3 files changed, 112 insertions(+), 118 deletions(-) diff --git a/src/PlaywrightSharp.Nunit/TestExpectations/TestExpectations.local.json b/src/PlaywrightSharp.Nunit/TestExpectations/TestExpectations.local.json index 42a60d767..ac7b58625 100644 --- a/src/PlaywrightSharp.Nunit/TestExpectations/TestExpectations.local.json +++ b/src/PlaywrightSharp.Nunit/TestExpectations/TestExpectations.local.json @@ -149,12 +149,6 @@ "parameters": ["headful"], "expectations": ["FAIL"] }, - { - "testIdPattern": "[elementhandle-convenience.spec.ts] *", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["headful"], - "expectations": ["FAIL"] - }, { "testIdPattern": "[elementhandle-eval-on-selector.spec.ts] *", "platforms": ["darwin", "linux", "win32"], diff --git a/src/PlaywrightSharp.Tests/ElementHandleConvenienceTests.cs b/src/PlaywrightSharp.Tests/ElementHandleConvenienceTests.cs index 974f83feb..02a454496 100644 --- a/src/PlaywrightSharp.Tests/ElementHandleConvenienceTests.cs +++ b/src/PlaywrightSharp.Tests/ElementHandleConvenienceTests.cs @@ -32,7 +32,9 @@ public async Task GetAttributeShouldWork() var handle = await Page.QuerySelectorAsync("#outer"); Assert.That(await handle.GetAttributeAsync("name"), Is.EqualTo("value")); + Assert.That(await handle.GetAttributeAsync("foo"), Is.Null); Assert.That(await Page.GetAttributeAsync("#outer", "name"), Is.EqualTo("value")); + Assert.That(await Page.GetAttributeAsync("#outer", "foo"), Is.Null); } [PlaywrightTest("elementhandle-convenience.spec.ts", "innerHTML should work")] @@ -57,7 +59,7 @@ public async Task InnerTextShouldWork() Assert.That(await Page.InnerTextAsync("#inner"), Is.EqualTo("Text, more text")); } - [PlaywrightTest("elementhandle-convenience.spec.ts", "'innerText should throw")] + [PlaywrightTest("elementhandle-convenience.spec.ts", "innerText should throw")] [Test, Timeout(TestConstants.DefaultTestTimeout)] public async Task InnerTextShouldThrow() { @@ -67,7 +69,7 @@ public async Task InnerTextShouldThrow() var handle = await Page.QuerySelectorAsync("svg"); var exception2 = Assert.CatchAsync(() => handle.InnerTextAsync()); - Assert.That(exception1.Message, Does.Contain("Not an HTMLElement")); + Assert.That(exception2.Message, Does.Contain("Not an HTMLElement")); } [PlaywrightTest("elementhandle-convenience.spec.ts", "textContent should work")] @@ -75,118 +77,10 @@ public async Task InnerTextShouldThrow() public async Task TextContentShouldWork() { await Page.GoToAsync(TestConstants.ServerUrl + "/dom.html"); - var handle = await Page.QuerySelectorAsync("#outer"); + var handle = await Page.QuerySelectorAsync("#inner"); Assert.That(await handle.TextContentAsync(), Is.EqualTo("Text,\nmore text")); - Assert.That(await Page.TextContentAsync("#outer"), Is.EqualTo("Text,\nmore text")); - } - - [PlaywrightTest("elementhandle-convenience.spec.ts", "Page.dispatchEvent(click)", "textContent should be atomic")] - [Test, Timeout(TestConstants.DefaultTestTimeout)] - public async Task TextContentShouldBeAtomic() - { - const string createDummySelector = @"({ - create(root, target) { }, - query(root, selector) { - const result = root.querySelector(selector); - if (result) - Promise.resolve().then(() => result.textContent = 'modified'); - return result; - }, - queryAll(root, selector) { - const result = Array.from(root.querySelectorAll(selector)); - for (const e of result) - Promise.resolve().then(() => e.textContent = 'modified'); - return result; - } - })"; - - await TestUtils.RegisterEngineAsync(Playwright, "textContent", createDummySelector); - await Page.SetContentAsync("
Hello
"); - string tc = await Page.TextContentAsync("textContent=div"); - Assert.That(tc, Is.EqualTo("Hello")); - Assert.That(await Page.EvaluateAsync("() => document.querySelector('div').textContent"), Is.EqualTo("modified")); - } - - [PlaywrightTest("elementhandle-convenience.spec.ts", "Page.dispatchEvent(click)", "innerText should be atomic")] - [Test, Timeout(TestConstants.DefaultTestTimeout)] - public async Task InnerTextShouldBeAtomic() - { - const string createDummySelector = @"({ - create(root, target) { }, - query(root, selector) { - const result = root.querySelector(selector); - if (result) - Promise.resolve().then(() => result.textContent = 'modified'); - return result; - }, - queryAll(root, selector) { - const result = Array.from(root.querySelectorAll(selector)); - for (const e of result) - Promise.resolve().then(() => e.textContent = 'modified'); - return result; - } - })"; - - await TestUtils.RegisterEngineAsync(Playwright, "innerText", createDummySelector); - await Page.SetContentAsync("
Hello
"); - string tc = await Page.InnerTextAsync("innerText=div"); - Assert.That(tc, Is.EqualTo("Hello")); - Assert.That(await Page.EvaluateAsync("() => document.querySelector('div').textContent"), Is.EqualTo("modified")); - } - - [PlaywrightTest("elementhandle-convenience.spec.ts", "Page.dispatchEvent(click)", "innerHTML should be atomic")] - [Test, Timeout(TestConstants.DefaultTestTimeout)] - public async Task InnerHtmlShouldBeAtomic() - { - const string createDummySelector = @"({ - create(root, target) { }, - query(root, selector) { - const result = root.querySelector(selector); - if (result) - Promise.resolve().then(() => result.textContent = 'modified'); - return result; - }, - queryAll(root, selector) { - const result = Array.from(root.querySelectorAll(selector)); - for (const e of result) - Promise.resolve().then(() => e.textContent = 'modified'); - return result; - } - })"; - - await TestUtils.RegisterEngineAsync(Playwright, "innerHtml", createDummySelector); - await Page.SetContentAsync("
Hello
"); - string tc = await Page.InnerHTMLAsync("innerHtml=div"); - Assert.That(tc, Is.EqualTo("Hello")); - Assert.That(await Page.EvaluateAsync("() => document.querySelector('div').textContent"), Is.EqualTo("modified")); - } - - [PlaywrightTest("elementhandle-convenience.spec.ts", "Page.dispatchEvent(click)", "getAttribute should be atomic")] - [Test, Timeout(TestConstants.DefaultTestTimeout)] - public async Task GetAttributeShouldBeAtomic() - { - const string createDummySelector = @"({ - create(root, target) { }, - query(root, selector) { - const result = root.querySelector(selector); - if (result) - Promise.resolve().then(() => result.setAttribute('foo', 'modified')); - return result; - }, - queryAll(root, selector) { - const result = Array.from(root.querySelectorAll(selector)); - for (const e of result) - Promise.resolve().then(() => e.setAttribute('foo', 'modified')); - return result; - } - })"; - - await TestUtils.RegisterEngineAsync(Playwright, "getAttribute", createDummySelector); - await Page.SetContentAsync("
"); - string tc = await Page.GetAttributeAsync("getAttribute=div", "foo"); - Assert.That(tc, Is.EqualTo("Hello")); - Assert.That(await Page.EvaluateAsync("() => document.querySelector('div').getAttribute('foo')"), Is.EqualTo("modified")); + Assert.That(await Page.TextContentAsync("#inner"), Is.EqualTo("Text,\nmore text")); } [PlaywrightTest("elementhandle-convenience.spec.ts", "isVisible and isHidden should work")] @@ -204,6 +98,8 @@ public async Task IsVisibleAndIsHiddenShouldWork() Assert.That(await span.IsHiddenAsync(), Is.True); Assert.That(await Page.IsVisibleAsync("span"), Is.False); Assert.That(await Page.IsHiddenAsync("span"), Is.True); + Assert.That(await Page.IsVisibleAsync("no-such-element"), Is.False); + Assert.That(await Page.IsHiddenAsync("no-such-element"), Is.True); } [PlaywrightTest("elementhandle-convenience.spec.ts", "isEnabled and isDisabled should work")] diff --git a/src/PlaywrightSharp.Tests/SelectorsRegisterTests.cs b/src/PlaywrightSharp.Tests/SelectorsRegisterTests.cs index c566d4f24..a2a3fbf0d 100644 --- a/src/PlaywrightSharp.Tests/SelectorsRegisterTests.cs +++ b/src/PlaywrightSharp.Tests/SelectorsRegisterTests.cs @@ -78,6 +78,110 @@ public async Task ShouldWorkInMainAndIsolatedWorld() Assert.That(await Page.EvalOnSelectorAsync("main=ignored >> css=section", "e => e.nodeName"), Is.EqualTo("SECTION")); } + [PlaywrightTest("selectors-register.spec.ts", "textContent should be atomic")] + [Test, Timeout(TestConstants.DefaultTestTimeout)] + public async Task TextContentShouldBeAtomic() + { + const string createDummySelector = @"({ + query(root, selector) { + const result = root.querySelector(selector); + if (result) + Promise.resolve().then(() => result.textContent = 'modified'); + return result; + }, + queryAll(root, selector) { + const result = Array.from(root.querySelectorAll(selector)); + for (const e of result) + Promise.resolve().then(() => e.textContent = 'modified'); + return result; + } + })"; + + await TestUtils.RegisterEngineAsync(Playwright, "textContent", createDummySelector); + await Page.SetContentAsync("
Hello
"); + string tc = await Page.TextContentAsync("textContent=div"); + Assert.That(tc, Is.EqualTo("Hello")); + Assert.That(await Page.EvaluateAsync("() => document.querySelector('div').textContent"), Is.EqualTo("modified")); + } + + [PlaywrightTest("selectors-register.spec.ts", "innerText should be atomic")] + [Test, Timeout(TestConstants.DefaultTestTimeout)] + public async Task InnerTextShouldBeAtomic() + { + const string createDummySelector = @"({ + query(root, selector) { + const result = root.querySelector(selector); + if (result) + Promise.resolve().then(() => result.textContent = 'modified'); + return result; + }, + queryAll(root, selector) { + const result = Array.from(root.querySelectorAll(selector)); + for (const e of result) + Promise.resolve().then(() => e.textContent = 'modified'); + return result; + } + })"; + + await TestUtils.RegisterEngineAsync(Playwright, "innerText", createDummySelector); + await Page.SetContentAsync("
Hello
"); + string tc = await Page.InnerTextAsync("innerText=div"); + Assert.That(tc, Is.EqualTo("Hello")); + Assert.That(await Page.EvaluateAsync("() => document.querySelector('div').textContent"), Is.EqualTo("modified")); + } + + [PlaywrightTest("selectors-register.spec.ts", "innerHTML should be atomic")] + [Test, Timeout(TestConstants.DefaultTestTimeout)] + public async Task InnerHtmlShouldBeAtomic() + { + const string createDummySelector = @"({ + query(root, selector) { + const result = root.querySelector(selector); + if (result) + Promise.resolve().then(() => result.textContent = 'modified'); + return result; + }, + queryAll(root, selector) { + const result = Array.from(root.querySelectorAll(selector)); + for (const e of result) + Promise.resolve().then(() => e.textContent = 'modified'); + return result; + } + })"; + + await TestUtils.RegisterEngineAsync(Playwright, "innerHtml", createDummySelector); + await Page.SetContentAsync("
Hello
"); + string tc = await Page.InnerHTMLAsync("innerHtml=div"); + Assert.That(tc, Is.EqualTo("Hello")); + Assert.That(await Page.EvaluateAsync("() => document.querySelector('div').textContent"), Is.EqualTo("modified")); + } + + [PlaywrightTest("selectors-register.spec.ts", "getAttribute should be atomic")] + [Test, Timeout(TestConstants.DefaultTestTimeout)] + public async Task GetAttributeShouldBeAtomic() + { + const string createDummySelector = @"({ + query(root, selector) { + const result = root.querySelector(selector); + if (result) + Promise.resolve().then(() => result.setAttribute('foo', 'modified')); + return result; + }, + queryAll(root, selector) { + const result = Array.from(root.querySelectorAll(selector)); + for (const e of result) + Promise.resolve().then(() => e.setAttribute('foo', 'modified')); + return result; + } + })"; + + await TestUtils.RegisterEngineAsync(Playwright, "getAttribute", createDummySelector); + await Page.SetContentAsync("
"); + string tc = await Page.GetAttributeAsync("getAttribute=div", "foo"); + Assert.That(tc, Is.EqualTo("Hello")); + Assert.That(await Page.EvaluateAsync("() => document.querySelector('div').getAttribute('foo')"), Is.EqualTo("modified")); + } + [PlaywrightTest("selectors-register.spec.ts", "should handle errors")] [Test, Timeout(TestConstants.DefaultTestTimeout)] public async Task ShouldHandleErrors()