DevExpress / DevExpress/testcafe

Chrome/Edge >=146 throws: TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')

Open
#8,493 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

TYPE: bug
Dominant language
JavaScript
Stars
9.9k
Forks
676
PR merge metrics
No merged PRs in 30d

Description

What is your Scenario?

Our tests run on a (self-hosted) headless azure-pipeline agent.
They started to fail when we updated the browsers to Chrome/Edge 146.
I can't reproduce the problem locally.
The problem appears for tests in our on-premise application - not for very basic pages.
Hence I can't create a minimal working example, because I have no way to properly debug the app, when the error occurs.

What is the Current behavior?

About 90% of our tests fail on one of the first actions/assertions of the test.

The error message is:

      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

BTW: The stack trace is only available if I run chrome with the --disable-web-security flag.

What is the Expected behavior?

The tests should not fail because of the exception in Hammerhead.
There should not be an exception like this in Hammerhead.

What is the public URL of the test page? (attach your complete example)
  • There is none
  • This is a complex on-premise application
  • I cant create a minimal working example because the tests only fail in our pipeline, where I can't use a debugger.
What is your TestCafe test code?
import { Selector } from "testcafe";

fixture("Dummy")
	.page(`http://localhost:16700/index.htm`)
	.before(async() => {
		// a simplification to report the bug
		await new Promise<void>((resolve, reject) => {
			setTimeout(resolve, 120_000);
		});
	})
	.beforeEach(async(t) => {
		// await t.debug();
		await t.resizeWindow(1024, 768);
	})
	.afterEach(async(t) => {
		// we need this later
		console.log(await t.getBrowserConsoleMessages());
	});

for (let i = 1; i <= 10; i++) {
	test(`test (Attempt #${i})`, async(t) => {
		await t.expect(Selector(`[pweb-name="NavigationCurrentView"]`).textContent).eql("0");
	});
}
Your complete configuration file

none

Your complete test report
> testcafe chrome:"azure-pipelines/PortableBrowsers/chrome/chrome-146-72/GoogleChromePortable.exe --disable-web-security" --headless ./Integrationstestprojekte/WebVisuPictures/dummy.tc.ts

 Running tests in:
 - Chrome 146.0.0.0 / Windows Server 2022

 Dummy
{ log: [], info: [], warn: [], error: [] }
 √ test (Attempt #1)
 × test (Attempt #2)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)


 × test (Attempt #3)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)


 × test (Attempt #4)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)


 × test (Attempt #5)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)


 × test (Attempt #6)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)


 × test (Attempt #7)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)


 × test (Attempt #8)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)


 × test (Attempt #9)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)


 × test (Attempt #10)

   1) - Error in fixture.beforeEach hook -
      A JavaScript error occurred on "http://localhost:16700/index.htm".
      Repeat test actions in the browser and check the console for errors.
      Enable the “skipJsErrors” option to ignore JavaScript errors during test execution. Learn more: "https://testcafe.io/documentation/404038/recipes/debugging/skip-javascript-errors"
      If the website only throws this error when you test it with TestCafe, please create a new issue at:
      "https://github.com/DevExpress/testcafe/issues/new?template=bug-report.md".

      JavaScript error details:
      TypeError: Cannot read properties of null (reading 'hammerhead|shadow-ui-element')
          at isShadowUIElement (http://localhost:56889/hammerhead.js:3583:22)
          at _onFocus (http://localhost:56889/hammerhead.js:40330:52)
          at http://localhost:56889/hammerhead.js:39672:34

      Browser: Chrome 146.0.0.0 / Windows Server 2022

          8 |            setTimeout(resolve, 120_000);
          9 |        });
         10 |    })
         11 |    .beforeEach(async(t) => {
         12 |        // await t.debug();
       > 13 |        await t.resizeWindow(1024, 768);
         14 |    })
         15 |    .afterEach(async(t) => {
         16 |        // we need this later
         17 |        console.log(await t.getBrowserConsoleMessages());
         18 |    });

         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:13:11)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:8:71)
         at __awaiter (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:4:12)
         at <anonymous> (C:\a\2\s\Integrationstestprojekte\WebVisuPictures\dummy.tc.ts:11:25)



 9/10 failed (2m 26s)
Screenshots

No response

Steps to Reproduce

none

TestCafe version

3.7.4

Node.js version

20.20.1

Command-line arguments

testcafe chrome:"azure-pipelines/PortableBrowsers/chrome/chrome-146-72/GoogleChromePortable.exe --disable-web-security" --headless ./Integrationstestprojekte/WebVisuPictures/dummy.tc.ts

Browser name(s) and version(s)

Chrome 146, Edge 146

Platform(s) and version(s)

Windows Server 2022

Other

I created a patch for testcafe-hammerhead, that fixes the problem and logs some debug-information:

diff --git a/node_modules/testcafe-hammerhead/lib/client/hammerhead.min.js b/node_modules/testcafe-hammerhead/lib/client/_hammerhead.min.js
similarity index 100%
rename from node_modules/testcafe-hammerhead/lib/client/hammerhead.min.js
rename to node_modules/testcafe-hammerhead/lib/client/_hammerhead.min.js
diff --git a/node_modules/testcafe-hammerhead/lib/client/hammerhead.js b/node_modules/testcafe-hammerhead/lib/client/hammerhead.js
index c01ff46..d0d65c3 100644
--- a/node_modules/testcafe-hammerhead/lib/client/hammerhead.js
+++ b/node_modules/testcafe-hammerhead/lib/client/hammerhead.js
@@ -3218,12 +3218,25 @@
 	    var doc = currentDocument || document;
 	    var activeElement = nativeMethods.documentActiveElementGetter.call(doc);
 	    var el = isDomElement(activeElement) ? activeElement : doc.body;
-	    while (el && el.shadowRoot) {
+		var result = el;
+	    while (result && result.shadowRoot) {
 	        // eslint-disable-next-line no-restricted-properties
-	        var shadowEl = el.shadowRoot.activeElement;
+	        var shadowEl = result.shadowRoot.activeElement;
 	        if (!shadowEl)
 	            break;
-	        el = shadowEl;
+	        result = shadowEl;
+	    }
+	    if(!result) {
+	        const info = {
+	            currentDocument: String(currentDocument),
+	            doc: String(doc),
+	            activeElement: String(activeElement),
+	            activeElementIsDomElement: String(isDomElement(activeElement)),
+	            el: String(el),
+	            result: String(result),
+	        }
+	        const err = new Error(`Unexpected nullish result for: getActiveElement(): ${JSON.stringify(info, null, 4)}`);
+	        console.error("getActiveElement", err.stack);
 	    }
 	    return el;
 	}
@@ -40327,6 +40340,11 @@
 	        this._onFocus = function (e) {
 	            var focusedEl = nativeMethods.eventTargetGetter.call(e);
 	            var activeEl = getActiveElement(document);
+	            if (activeEl === null) {
+	                const err = new Error(`getActiveElement() returned null for ${String(document)}!`);
+	                console.error("_onFocus", err.stack);
+	                return // gracefully return instead of throwing in isShadowUIElement(activeEl)
+	            };
 	            if (!isShadowUIElement(focusedEl) && !isShadowUIElement(activeEl))
 	                shadowUI.setLastActiveElement(activeEl);
 	        };

As a result, we log this:

getActiveElement Error: Unexpected nullish result for: getActiveElement(): {
    "currentDocument": "null",
    "doc": "[object HTMLDocument]",
    "activeElement": "null",
    "activeElementIsDomElement": "null",
    "el": "null",
    "result": "null"
}
    at getActiveElement (http://localhost:51773/hammerhead.js:3238:22)
    at _onFocus (http://localhost:51773/hammerhead.js:40342:29)
    at http://localhost:51773/hammerhead.js:39685:34


_onFocus Error: getActiveElement() returned null for null!
    at _onFocus (http://localhost:51773/hammerhead.js:40344:30)
    at http://localhost:51773/hammerhead.js:39685:34


getActiveElement Error: Unexpected nullish result for: getActiveElement(): {
    "currentDocument": "[object HTMLDocument]",
    "doc": "[object HTMLDocument]",
    "activeElement": "null",
    "activeElementIsDomElement": "null",
    "el": "null",
    "result": "null"
}
    at getActiveElement (http://localhost:51773/hammerhead.js:3238:22)
    at http://localhost:51773/hammerhead.js:39295:34
    at http://localhost:51773/hammerhead.js:39685:34

The problem is, that nativeMethods.documentActiveElementGetter.call(doc); returns null.

According to MDN null is a valid value:

If the focused element is within a document tree that's not descended from the current document (for example, the focused element is in the main document, and the invoking document is an embedded iframe), then this will be null.

I honestly don't know why this happens, but it is a valid value and should be handled properly.
IMO isShadowUIElement(element) should return false if element is null.

There are no iframes in our application.

Given that not all tests fail, I suppose this is a timing issue. Chrome 146 probably does things a little different, so this fails.
But of course this could also be a bug in chrome.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reported stack in hammerhead.js, especially isShadowUIElement and _onFocus, and reproduce the failure using the provided TestCafe fixture with Chrome/Edge 146 and the resizeWindow step. Compare behavior across browser versions and investigate why the referenced object is null. Done means the fixture runs without the Hammerhead TypeError.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
testing-qa, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.