9

I am new to UI test cases. what I am trying to do is to validate text of button in my angular template.

Below are the button controls available in my angular template file

<button type="button" class="btn btn-primary" (click)="staticModal.show()">Show/Hide</button>
&nbsp;&nbsp;
<button type="button" class="btn btn-primary" (click)="filterModal.show()">Filter</button>

Below is my angular test

it('should have button with text show/hide',()=>{
    const debug=fixture.debugElement;
    const native=debug.nativeElement;
    const buttonElem=native.querySelector('button:contains("show/hide")');
    console.log(native);
    expect(buttonElem.textContent).toContain('Show/Hide');
  });

As I have two buttons in my template without any Id and with same class. I am trying to find right button with text value but it is failing with below error "SyntaxError: Failed to execute 'querySelector' on 'Element': 'button:contains("show/hide")' is not a valid selector."

What is the right way to test this scenario.

Edit: I am using Jasmine with karma for testing.

Anil
  • 1,669
  • 5
  • 19
  • 44
  • What testing framework do you use? Protractor? –  Jun 07 '18 at 21:20
  • I am using jasmine with karma – Anil Jun 07 '18 at 21:21
  • did you try .querySelector('button[textContent=show/hide]'); – Fateh Mohamed Jun 07 '18 at 21:35
  • 1
    If you are selecting by the text value, then checking that text value...I'm not sure what you are testing. The test won't pass if the element is not found, since your test and selection rely on the exact same thing. – Ash Jun 08 '18 at 01:49
  • @Fateh Mohamed: yes I tried it didn't work. – Anil Jun 08 '18 at 23:35
  • @Anil based only on what code you have shared, I think selecting the element via a different query should be sufficient. Adding a unique class or id would be easiest, however using `first-child`, `first-of-type`, or `nth-child()` should work. If the text is that important and there is functionality behind the buttons, consider displaying a single word and changing the text value on a click/toggle action. Then you could check the value/property in a new test, for example, `isModalVisible = true`. – Ash Jun 11 '18 at 12:25

4 Answers4

7

Since neither fixture.debugElement.query(By.css('button[textContent="Show/Hide"]') nor fixture.nativeElement.querySelector('button[textContent="Show/Hide"]') works for me, I use a predicate function for that:

const buttonDebugElement = fixture.debugElement.query(
  debugEl => debugEl.name === 'button' && debugEl.nativeElement.textContent === 'Show/Hide'
);

It is described in the Angular Docs here: https://angular.io/guide/testing-utility-apis#debugelement

Another way using the By.css helper and queryAll:

const buttonDebugElement = fixture.debugElement.queryAll(By.css('button').find(
  buttonDebugEl => buttonDebugEl.nativeElement.textContent === 'Show/Hide'
);

Be aware that the text comparison is case sensitive!

Finn Mathis
  • 71
  • 1
  • 2
1

try this

it('should have button with text show/hide',()=>{
 const debug=fixture.debugElement;
 const native=debug.nativeElement;
 const buttonElem=debug.query(By.css('button[textContent=show/hide]')); // change selector here
 expect(buttonElem.textContent).toContain('Show/Hide');
});
Fateh Mohamed
  • 20,445
  • 5
  • 43
  • 52
  • 1
    this solution didn't work. There was typo error with "show/hide" but even after using correct value it didn't work. property "textContent" doesn't work on debugElement. So I did query on deugElement and then tried nativeElement.textContent. Getting error "Cannot read property 'nativeElement' of null" – Anil Jun 07 '18 at 22:34
1

Providing answer for my own question. I tried below solution and it worked for me. Not sure if this is best approach.

First I updated my HTML code. converted "button" element to "input" type element.

<input type="button" class="btn btn-primary" (click)="staticModal.show()" value="Show/Hide"/>
&nbsp;&nbsp;
<button type="button" class="btn btn-primary" (click)="filterModal.show()">Filter</button>

second I modified my test case as below.

it('should have button with text show/hide',()=>{
    const debug=fixture.debugElement.query(By.css('input[value="Show/Hide"]'));
    const native=debug.nativeElement;
    const buttonElem=native;    
    expect(buttonElem.value).toContain('Show/Hide');
  });

This worked for me.

Anil
  • 1,669
  • 5
  • 19
  • 44
1

One could also use XPATH expressions. For more information, view this post: https://stackoverflow.com/a/37098628/10315665.

And the potential solution to the question:

const button = document
  .evaluate("//button[text()='Show/Hide']", document)
  .iterateNext();

console.log(button);
<button type="button" class="btn btn-primary" (click)="staticModal.show()">Show/Hide</button>
&nbsp;&nbsp;
<button type="button" class="btn btn-primary" (click)="filterModal.show()">Filter</button>

Of course, in actual testing, you would probably want to provide a different contextNode than document. A safe candidate would be fixture.nativeElement:

document
  .evaluate("//button[text()='Show/Hide']", fixture.nativeElement)
  .iterateNext();
Elias
  • 3,592
  • 2
  • 19
  • 42