3

I'm converting a bunch of tests from Selenium + Jest to Playwright + Jest, and one of the differences is that for element lookups, await page.$(".whatever") doesn't reject if it doesn't find the element.

I'm doing a lot of element lookups as we've built out Page Object Models for a lot of components.

My question is, do I need to manually add some error handling, or am I missing something here? Jest will tell me which test fails but there's no stack trace so it can be hard to track down what went wrong. Also, is this a good way to do it?

// some-test.js
it("should display the event title", async () => {
  let eventTitle = await this.bookingPage.getEventTitle();
  expect(await eventTitle.textContent()).toBe("Some special event");
});

// Page Object Model

class BookingPage extends PageObjectModel {
  async getEventTitle() {
    const selector = "h2.title .content";
    const result = await page.$(selector);
    if (!result) throw new Error(`Could not find selector: "${selector}"`);
    return result;
  }
}

If it is a good approach it seems like a pain to have to do for all lookups. Would it be a good idea to wrap page.$ in another function that just does it for me? It would be a bad idea to mutate page.$ itself right? So maybe create a wrapper method that does the error handling and it and then calls page.$?

So I could do something like this:

class PageObjectModel {
  async $(selector) {
    const result = await page.$(selector);
    if (!result) throw new Error(`Could not find selector: "${selector}"`);
    return result;
  }
}

class BookingPage extends PageObjectModel {
  async getEventTitle() {
    return this.$("h2.title .content");
  }
}

Which is kinda nice but then for some reason the stack trace only links to some-test.js and PageObjectModel.js, and not to BookingPage.js.

I feel like I'm not missing something about JS error handling in general? ¯_(ツ)_/¯

1 Answer 1

3

Okay so in the end I found that you can use page.waitForSelector which will also return the element if it's found and will reject with a TimeoutError if it's not found, which is what I wanted.

For some reason the stack-trace still only links to some-test.js and PageObjectModel.js however if I catch and re-throw it in BookingPage.js then it does add that to the stack trace.

I don't actually need to throw it, I just need to log it out. So this is the solution I'm going with:

// some-test.js - ** unchanged **
it("should display the event title", async () => {
  let eventTitle = await this.bookingPage.getEventTitle();
  expect(await eventTitle.textContent()).toBe("Some special event");
});
class PageObjectModel {
  async $(selector) {
    return page.waitForSelector(selector);
  }
}
class BookingPage extends PageObjectModel {
  async getEventTitle() {
    return await this.$("h2.title .content").catch(console.error);
  }
}

It's a bit weird as according to this article await return is redundant outside of try/catch, but I guess because I'm using .catch it's functionally the same as try catch?

Anyway, this gives me the stack trace that I want.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.