salesforce / kagekiri

Shadow DOM-piercing query APIs for the browser.
BSD 3-Clause "New" or "Revised" License
86 stars 9 forks source link

Protractor e2e test with kagekiri gives "Element is not defined" error #13

Closed Manojms1997 closed 4 years ago

Manojms1997 commented 4 years ago

Issue:

E2E testing with protractor, using kagekiri's querySelector() and querySelectorAll() is giving "Element is not defined" error and pointing to kagekiri's CommonJS file. This issue is not present in karma and jasmine based unit test.

Reproduction:

The DOM structure of angular's AppComponent is:

<div class="first">
  first
  <div class="second">second</div>
</div>

Contents of AppComponent is inside shadow DOM as encapsulation is set to ViewEncapsulation.ShadowDom.

E2E test:

In app.e2e.spec.ts file, querySelector and querySelectorAll methods are imported

import { querySelector, querySelectorAll } from 'kagekiri';

The following test case is written in e2e test:

  it("should get div with class second from kagekiri queryselector", async() => {
    page.navigateTo();
    const secondDiv = querySelector('.second');
    const divText = secondDiv.textContent;
    expect(divText).toEqual('second');
  });

When running end to end test for this application, I am getting the following error: image

But the same test in karma based unit test is passing. This same issue was faced when using test harness which contains kagekiri, being used in protractor e2e tests.

The dependencies and dev-dependencies are:

 "dependencies": {
    "@angular/animations": "~9.1.1",
    "@angular/common": "~9.1.1",
    "@angular/compiler": "~9.1.1",
    "@angular/core": "~9.1.1",
    "@angular/forms": "~9.1.1",
    "@angular/platform-browser": "~9.1.1",
    "@angular/platform-browser-dynamic": "~9.1.1",
    "@angular/router": "~9.1.1",
    "rxjs": "~6.5.4",
    "tslib": "^1.10.0",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.901.1",
    "@angular/cli": "~9.1.1",
    "@angular/compiler-cli": "~9.1.1",
    "@angular/language-service": "~9.1.1",
    "@types/jasmine": "~3.5.0",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^12.11.1",
    "codelyzer": "^5.1.2",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "kagekiri": "^1.0.19",
    "karma": "~4.4.1",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~3.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
    "protractor": "~5.4.3",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "~3.8.3"
  }

You can find the sample application, having this issue, in the following github link: https://github.com/Manojms1997/KagekiriEndToEnd.git

nolanlawson commented 4 years ago

As far as I can tell, this is not a kagekiri issue. What's happening in this test is that, somehow, the module is being executed in a Node.js environment rather than a browser environment. Element is undefined in Node.js (as is document, window, etc.), so the test fails. (I can tell this is true because I can add console.logs to kagekiri.cjs.js showing that it is a Node environment, e.g. console.log(typeof process).)

You could test this by not using kagekiri at all, but adding some very simple assertion to your component such as:

if (typeof window === 'undefined') {
  throw new Error('no window!')
}

This should still fail your test.

I'm not an expert in Angular or Protractor so I can't really help with the configuration, but it needs to either execute in a browser environment or jsdom, etc. kagekiri is not designed to run in Node.js but rather in a browser environment.

Hope that helps, closing the issue for now!

nolanlawson commented 4 years ago

BTW thank you for including a reproducible test case; that is very helpful! 🙂

nolanlawson commented 4 years ago

I believe I see the issue now. It looks like this line of code is importing kagekiri in a Node.js environment. It looks like these Protractor tests run in Node.js but send commands to the browser. So browser code cannot run directly in these files, but rather has to be injected (e.g. with executeScript()).

Manojms1997 commented 4 years ago

@nolanlawson Thank you for giving more insight on the issue. I tried using executeScript() to run querySelector(), but I am facing some more errors. I will continue looking into the issue. It would be helpful, if I can get a sample application using kagekiri in protractor e2e.

Manojms1997 commented 4 years ago

Hello @nolanlawson , Thank you for suggesting executeScript() to load kagekiri on browser side. It worked. I am using browser.executeScript() to load kagekiri file, in this line of code. But in order to load kagekiri, it looks like I should have kagekiri.umd.js file accessible from my application (right now I have added it in assets folder). Is there any better way to inject kagekiri.js on the browser side. Also I have updated https://github.com/Manojms1997/KagekiriEndToEnd.git for reference.

PS: We are actually looking for shadow DOM piercing approach for our component harness. So on the suggestion from this issue we are using kagekiri as query function for our harness loader.

nolanlawson commented 4 years ago

Yes, loading kagekiri into the browser is the best way to use it in this kind of webdriver-testing approach. Based on the angular/components issue you linked to, it looks like they are injecting kagekiri like this.

The technique you are using looks like it should work too. :) Dropping in kagekiri.umd.js is an easy way to access the global kagekiri.querySelector/kagekiri.querySelectorAll functions. Glad you got it working. :)